Skip to content

Commit c67e334

Browse files
committed
Merge branch 'en/rust-wip' into seen
* en/rust-wip: misc::varint: reimplement as test balloon for Rust misc: use BuildHelper build: new crate, misc varint: use explicit width for integers build-helper: cbindgen, let crates generate a header file build-helper: link against libgit.a and any other required C libraries build: new crate, build-helper github workflows: upload Cargo.lock win+Meson: do allow linking with the Rust-built xdiff github workflows: install rust help: report on whether or not Rust is enabled build: introduce rust BreakingChanges: announce Rust becoming mandatory doc: add a policy for using Rust make: merge reftable lib into libgit.a make: merge xdiff lib into libgit.a make: add -fPIE flag cleanup: rename variables that collide with Rust primitive type names
2 parents d5a079d + fff596e commit c67e334

36 files changed

Lines changed: 896 additions & 105 deletions

.github/workflows/main.yml

Lines changed: 81 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.63.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,28 @@ 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+
with:
127+
flavor: full
128+
- name: Install rustup via github actions
129+
uses: actions-rs/toolchain@v1
130+
with:
131+
toolchain: stable
132+
profile: minimal
133+
override: false
134+
- name: Install Rust toolchain
135+
shell: bash
136+
env:
137+
RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_windows }}
138+
RUST_TARGET: ${{ needs.ci-config.outputs.rust_target_windows_make }}
139+
run: ci/install-rust-toolchain.sh
117140
- name: build
118141
shell: bash
119142
env:
@@ -127,6 +150,11 @@ jobs:
127150
with:
128151
name: windows-artifacts
129152
path: artifacts
153+
- name: upload Cargo.lock
154+
uses: actions/upload-artifact@v4
155+
with:
156+
name: cargo-lock-windows
157+
path: rust/Cargo.lock
130158
windows-test:
131159
name: win test
132160
runs-on: windows-latest
@@ -254,12 +282,26 @@ jobs:
254282
needs: ci-config
255283
if: needs.ci-config.outputs.enabled == 'yes'
256284
runs-on: windows-latest
285+
env:
286+
CARGO_HOME: "/c/Users/runneradmin/.cargo"
257287
concurrency:
258288
group: windows-meson-build-${{ github.ref }}
259289
cancel-in-progress: ${{ needs.ci-config.outputs.skip_concurrent == 'yes' }}
260290
steps:
261291
- uses: actions/checkout@v4
262292
- uses: actions/setup-python@v5
293+
- name: Install rustup via github actions
294+
uses: actions-rs/toolchain@v1
295+
with:
296+
toolchain: stable
297+
profile: minimal
298+
override: false
299+
- name: Install Rust toolchain
300+
shell: bash
301+
env:
302+
RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_windows }}
303+
RUST_TARGET: ${{ needs.ci-config.outputs.rust_target_windows_meson }}
304+
run: ci/install-rust-toolchain.sh
263305
- name: Set up dependencies
264306
shell: pwsh
265307
run: pip install meson ninja
@@ -274,6 +316,11 @@ jobs:
274316
with:
275317
name: windows-meson-artifacts
276318
path: build
319+
- name: Upload Cargo.lock
320+
uses: actions/upload-artifact@v4
321+
with:
322+
name: cargo-lock-windows-meson
323+
path: rust/Cargo.lock
277324
windows-meson-test:
278325
name: win+Meson test
279326
runs-on: windows-latest
@@ -329,11 +376,24 @@ jobs:
329376
jobname: ${{matrix.vector.jobname}}
330377
CI_JOB_IMAGE: ${{matrix.vector.pool}}
331378
TEST_OUTPUT_DIRECTORY: ${{github.workspace}}/t
379+
CARGO_HOME: "/Users/runner/.cargo"
332380
runs-on: ${{matrix.vector.pool}}
333381
steps:
334382
- uses: actions/checkout@v4
335383
- run: ci/install-dependencies.sh
336-
- run: ci/run-build-and-tests.sh
384+
- name: Install rustup via github actions
385+
uses: actions-rs/toolchain@v1
386+
with:
387+
toolchain: stable
388+
profile: minimal
389+
override: false
390+
- name: Install Rust toolchain
391+
shell: bash
392+
env:
393+
RUST_VERSION: ${{ needs.ci-config.outputs.rust_version_minimum }}
394+
run: ci/install-rust-toolchain.sh
395+
- name: Run build and tests
396+
run: ci/run-build-and-tests.sh
337397
- name: print test failures
338398
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
339399
run: ci/print-test-failures.sh
@@ -343,6 +403,11 @@ jobs:
343403
with:
344404
name: failed-tests-${{matrix.vector.jobname}}
345405
path: ${{env.FAILED_TEST_ARTIFACTS}}
406+
- name: Upload Cargo.lock
407+
uses: actions/upload-artifact@v4
408+
with:
409+
name: cargo-lock-${{matrix.vector.jobname}}
410+
path: rust/Cargo.lock
346411
fuzz-smoke-test:
347412
name: fuzz smoke test
348413
needs: ci-config
@@ -393,9 +458,11 @@ jobs:
393458
cc: gcc
394459
- jobname: linux-musl-meson
395460
image: alpine:latest
461+
rust_version_override: ${{ needs.ci-config.outputs.rust_version_musl }}
396462
# Supported until 2025-04-02.
397463
- jobname: linux32
398464
image: i386/ubuntu:focal
465+
rust_target_override: ${{ needs.ci-config.outputs.rust_target_32bit_linux }}
399466
- jobname: pedantic
400467
image: fedora:latest
401468
# A RHEL 8 compatible distro. Supported until 2029-05-31.
@@ -409,6 +476,7 @@ jobs:
409476
CC: ${{matrix.vector.cc}}
410477
CI_JOB_IMAGE: ${{matrix.vector.image}}
411478
CUSTOM_PATH: /custom
479+
CARGO_HOME: /home/builder/.cargo
412480
runs-on: ubuntu-latest
413481
container: ${{matrix.vector.image}}
414482
steps:
@@ -433,6 +501,13 @@ jobs:
433501
- run: ci/install-dependencies.sh
434502
- run: useradd builder --create-home
435503
- run: chown -R builder .
504+
- name: Install rustup via script
505+
run: sudo --preserve-env --set-home --user=builder ci/install-rustup.sh
506+
- name: Install Rust toolchain
507+
env:
508+
RUST_VERSION: ${{ matrix.vector.rust_version_override || needs.ci-config.outputs.rust_version_minimum }}
509+
RUST_TARGET: ${{ matrix.vector.rust_target_override || '' }}
510+
run: sudo --preserve-env --set-home --user=builder ci/install-rust-toolchain.sh
436511
- run: sudo --preserve-env --set-home --user=builder ci/run-build-and-tests.sh
437512
- name: print test failures
438513
if: failure() && env.FAILED_TEST_ARTIFACTS != ''
@@ -443,6 +518,11 @@ jobs:
443518
with:
444519
name: failed-tests-${{matrix.vector.jobname}}
445520
path: ${{env.FAILED_TEST_ARTIFACTS}}
521+
- name: Upload Cargo.lock
522+
uses: actions/upload-artifact@v4
523+
with:
524+
name: cargo-lock-${{matrix.vector.jobname}}
525+
path: rust/Cargo.lock
446526
static-analysis:
447527
needs: ci-config
448528
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/BreakingChanges.adoc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,41 @@ A prerequisite for this change is that the ecosystem is ready to support the
165165
"reftable" format. Most importantly, alternative implementations of Git like
166166
JGit, libgit2 and Gitoxide need to support it.
167167

168+
* Git will require Rust as a mandatory part of the build process. While Git
169+
already started to adopt Rust in Git 2.49, all parts written in Rust are
170+
optional for the time being. This includes:
171+
+
172+
** Subsystems that have an alternative implementation in Rust to test
173+
interoperability between our C and Rust codebase.
174+
** Newly written features that are not mission critical for a fully functional
175+
Git client.
176+
+
177+
These changes are meant as test balloons to allow distributors of Git to prepare
178+
for Rust becoming a mandatory part of the build process. There will be multiple
179+
milestones for the introduction of Rust:
180+
+
181+
--
182+
1. In Git 2.52, both build systems will default-enable support for Rust.
183+
Consequently, builds will break by default if Rust is not available on the
184+
build host. The use of Rust can still be explicitly disabled via build
185+
flags.
186+
2. In Git 3.0, the build options will be removed and support for Rust is
187+
mandatory.
188+
--
189+
+
190+
Disable building with Rust:
191+
Meson: `meson configure -Dwith_rust=false`.
192+
Makefile: `make WITH_RUST=false`,
193+
+
194+
The Git project will declare the last version before Git 3.0 to be a long-term
195+
support release. This long-term release will receive important bug fixes for at
196+
least four release cycles and security fixes for six release cycles. The Git
197+
project will hand over maintainership of the long-term release to distributors
198+
in case they need to extend the life of that long-term release even further. In
199+
that case, the backporting process will be handled by these distributors, but
200+
the backported patches will be reviewed on the mailing list and pulled in by the
201+
Git maintainer.
202+
168203
=== Removals
169204

170205
* Support for grafting commits has long been superseded by git-replace(1).

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.
102+
However, we may use bindgen and cbindgen to share existing Git types as an
103+
interim step.
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)