Почему Git workflow — это спасение для Rust-проекта (и вашей психики)
Вы когда-нибудь пытались откатить «сломанный» коммит в Rust-проекте, который случайно задевает src/main.rs, Cargo.toml и ещё три модуля? Знакомо? Если да — вы точно не одиноки. Rust славится своей строгостью, но без грамотного Git workflow даже самый идеальный код превращается в кашу.
В этой статье я покажу, как построить рабочий процесс с Git, который сэкономит вам часы дебага и нервов. Мы разберём реальные примеры на Rust, поговорим о CI/CD, code review и о том, как не потерять лицо перед коллегами. Поехали!
Основы Git workflow для Rust: с чего начать
Прежде чем нырять в детали, давайте определимся с базой. Для Rust-проектов я рекомендую использовать GitHub Flow или Git Flow — в зависимости от размера команды. Но есть одна особенность: Rust требует строгой типизации и тестов, поэтому ваш workflow должен быть заточен под cargo test и cargo clippy.
Структура веток для Rust
Вот минимальный набор веток, который я использую в своих проектах:
main— стабильная версия, всегда готова к релизуdevelop— интеграционная ветка для фичfeature/название-фичи— каждая новая фича изолированаhotfix/название— для срочных исправлений
Почему это важно для Rust? Потому что cargo check и cargo build могут выявить конфликты зависимостей, которые легче решить в изолированной ветке, чем в общей.
Пример: создаём feature-ветку для новой функции
Допустим, вы пишете парсер JSON на Rust. Вы начинаете с ветки main:
git checkout main
git pull origin main
git checkout -b feature/json-parser
Теперь вы пишете код. Вот простой пример функции, которую вы добавляете в src/parser.rs:
use serde_json::Value;
pub fn parse_json(input: &str) -> Result<Value, String> {
serde_json::from_str(input).map_err(|e| e.to_string())
}
Перед коммитом обязательно запустите тесты:
cargo test
cargo clippy -- -D warnings
Если всё ок — делаем коммит:
git add src/parser.rs Cargo.toml
git commit -m "feat: добавить базовый парсер JSON с обработкой ошибок"
Обратите внимание на формат коммита: feat: — это часть Conventional Commits, которые отлично работают с Rust-проектами, особенно если вы используете автоматический changelog.
CI/CD для Rust: автоматическая проверка кода
Без CI ваш Git workflow — просто набор веток. Для Rust я настоятельно рекомендую добавить в корень проекта файл .github/workflows/ci.yml (если используете GitHub):
name: Rust CI
on:
push:
branches: [ "main", "develop" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- name: Lint
run: cargo clippy -- -D warnings
- name: Format check
run: cargo fmt -- --check
Теперь каждый push в main или develop будет запускать сборку, тесты и линтинг. Если CI падает — коммит не мержится. Это железобетонная защита от «оно работает на моей машине».
Почему это спасёт ваш проект
Представьте: вы забыли обновить Cargo.lock после добавления зависимости. CI сразу это заметит — cargo build выдаст ошибку. Или вы случайно использовали unwrap() в продакшн-коде — clippy с флагом -D warnings заблокирует мерж.
Code review для Rust: на что обращать внимание
Code review — это не просто «посмотреть код». Для Rust есть специфические моменты, которые стоит проверять:
- Безопасность памяти: нет ли лишних
unsafeблоков? - Обработка ошибок: используете
Resultвместо паники? - Тесты: покрыты ли краевые случаи?
- Документация: есть ли
///комментарии для публичных функций?
Пример плохого кода, который должен провалить review:
// Плохо: unwrap() без обработки
let data = parse_json(input).unwrap();
Хороший вариант:
// Хорошо: обработка ошибки
match parse_json(input) {
Ok(data) => println!("Parsed: {}", data),
Err(e) => eprintln!("Failed to parse: {}", e),
}
При мерже пулл-реквеста используйте squash merge или rebase, чтобы история оставалась чистой. Для Rust-проектов я предпочитаю rebase — так каждый коммит проходит CI.
Работа с Cargo.toml и Cargo.lock в Git
Это частая боль. Cargo.lock нужно коммитить для приложений (бинарников), но не для библиотек. В Git workflow это означает:
- Если ваш проект — библиотека, добавьте
Cargo.lockв.gitignore - Если приложение — всегда коммитьте
Cargo.lock
Пример .gitignore для Rust-библиотеки:
/target
Cargo.lock
Для приложения:
/target
Когда вы меняете зависимости в Cargo.toml, обязательно обновляйте Cargo.lock через cargo update и коммитьте оба файла. Иначе коллеги получат разные версии зависимостей — и поехали баги.
Советы для новичков: как не наступить на грабли
Вот несколько лайфхаков, которые я вынес из своего опыта:
- Не коммитьте бинарники: добавьте
/targetв.gitignoreсразу при инициализации проекта - Используйте git hooks: например,
pre-commitсcargo fmtиcargo clippy - Пи