- 通用 GitLab 协作与项目治理(账号、分支、MR、CI/CD、合规、模板等);
- 面向 C 语言项目 的目录结构、Makefile 与 GitLab CI 模板(最小与增强版);
- 工作场景向 Git 命令速查(含冲突/回退/恢复的完整示例);
git checkout与git switch/git restore的一一对照与融入脚本。
.gitignore-片段git checkout 用法速查与对照git checkout 融入你的日常脚本目标: 身份明确、连接稳定、权限最小化。
bash展开代码git --version
git config --global user.name "张三"
git config --global user.email "zhangsan@your.org"
git config --global init.defaultBranch main
# 建议:开启签名提交(如可用 GPG/SSH 签名)
git config --global commit.gpgsign true
SSH Key(推荐)
bash展开代码ssh-keygen -t ed25519 -C "zhangsan@your.org"
# 将 ~/.ssh/id_ed25519.pub 添加至 GitLab -> User Settings -> SSH Keys
ssh -T git@gitlab.com # 或你们自建域名
访问令牌(HTTPS / API / CI 用)
read_repository/write_repository)。A)远端已有仓库 → 本地克隆
bash展开代码git clone git@gitlab.com:group/project.git
cd project
B)本地有代码 → 推送到新仓库
bash展开代码cd myproject
git init
git remote add origin git@gitlab.com:group/project.git
git add .
git commit -m "chore: initial commit"
git branch -M main
git push -u origin main
规范: 项目可见性设为 Private;保护
main/release/*分支(见 §5)。
分支角色建议
main:受保护(禁止直接 push,只能 MR 合并),稳定/生产。develop:集成分支(建议也受保护,MR 合并)。feature/*、fix/*:个人/任务分支,短期存在。起分支 → 提交 → 推送 → 发起 MR
bash展开代码# 从最新主线切出功能分支
git checkout main
git pull
git checkout -b feature/invoice-import
# 开发与提交(建议采用 Conventional Commits)
git add src/invoice_parser.py
git commit -m "feat(invoice): add excel importer with validation"
git push -u origin feature/invoice-import
在 GitLab 发起 MR
develop 或 main;填写描述、关联 Issue、勾选“删除源分支”;至少 1–2 人审核。保持分支更新(Rebase 保持整洁)
bash展开代码git fetch origin
git rebase origin/main # 在 feature 分支上
# 若有冲突:修复 -> git add -> git rebase --continue
git push --force-with-lease
查看状态/差异/历史
bash展开代码git status
git diff
git diff --staged
git log --oneline --graph --decorate --all
git show <commit>
git blame path/to/file
暂存/提交/修正
bash展开代码git add src/a.c include/a.h
git commit -m "feat(parser): add tokenizer with error codes"
git commit --amend # 修正上一条提交(未共享时用)
撤销暂存/工作区修改
bash展开代码git restore --staged src/a.c git restore src/a.c
忽略文件并清理历史中的已跟踪项
展开代码# .gitignore /build/ *.o *.a *.so
bash展开代码git rm -r --cached build
git commit -m "chore: apply .gitignore to build artifacts"
保护主线与审批
main、develop(禁止直接 push / 必须 MR)。CODEOWNERS(强制特定路径由特定人审核)
展开代码# 根目录新增 CODEOWNERS docs/** @finance-reviewer src/budget/** @lead-dev @qa
安全
bash展开代码git lfs install
git lfs track "*.xlsx"
git add .gitattributes
git add data/report.xlsx
git commit -m "chore(data): add LFS-tracked report"
git push
LFS 有存储/带宽成本;敏感信息建议脱敏或放对象存储,仅在仓库放引用。
通用要点
environments 与 manual 步骤做受控发布。Python 最小模板(可根据需要取用)
yaml展开代码stages: [test, build]
default:
image: python:3.11
cache:
paths: [ .cache/pip ]
before_script:
- pip install -U pip
- pip install -r requirements.txt
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
test:
stage: test
script:
- pytest -q
artifacts:
when: always
reports:
junit: junit.xml
build:
stage: build
script:
- python setup.py sdist bdist_wheel
artifacts:
paths:
- dist/
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
Android/Java 片段(示意)
yaml展开代码image: gradle:8-jdk17
stages: [build]
build:
stage: build
script:
- gradle assembleDebug
artifacts:
paths: [ app/build/outputs/apk/debug/ ]
C 项目 CI 请见 §14(最小与增强模板)。
提交信息(Conventional Commits)
feat: 新增发票导入校验fix: 修复汇总口径浮点精度问题chore(ci): 拆分测试与构建阶段MR 模板(.gitlab/merge_request_templates/默认.md)
md展开代码### 变更内容
-
### 关联 Issue
- Closes #
### 风险与影响
- 数据库/接口变更?
- 是否需要回滚方案?
### 校验清单
- [ ] 本地自测通过
- [ ] 单元测试通过
- [ ] 更新了文档/变更说明
Issue 模板(.gitlab/issue_templates/缺陷.md)
md展开代码### 现象
### 期望
### 复现步骤
### 截图/日志
### 影响范围
bash展开代码# 在 GitLab 上 fork 主仓库
git clone git@gitlab.com:you/project-fork.git
git remote add upstream git@gitlab.com:group/project.git
# 同步主仓
git fetch upstream
git checkout main
git merge upstream/main
git push origin main
# 在 fork 内开发并向主仓发 MR
git rm --cached + 立刻轮换密钥;必要时历史清理(慎重)。rebase origin/main。echo。.gitignore 片段gitignore展开代码# OS / IDE .DS_Store Thumbs.db .idea/ .vscode/ # C/C++ 构建 /build/ *.o *.a *.so *.dll *.dylib # 覆盖率 *.gcda *.gcno *.gcov /build/coverage* /build/coverage_html/ # 其他 dist/ node_modules/
展开代码project/ ├─ include/ # 头文件 │ └─ project.h ├─ src/ # 源文件 │ ├─ project.c │ └─ utils.c ├─ tests/ # 简单单元测试(main 方式或引入 CMocka/Unity) │ └─ test_project.c ├─ build/ # 编译输出(由 Makefile 生成) ├─ Makefile ├─ .gitignore └─ .clang-format # 代码风格可选
示例 Makefile(GCC)
makefile展开代码CC ?= gcc
CFLAGS ?= -O2 -Wall -Wextra -Werror -std=c11 -Iinclude
LDFLAGS ?=
SRC := $(wildcard src/*.c)
OBJ := $(patsubst src/%.c,build/%.o,$(SRC))
BIN := build/app
TEST_SRC := $(wildcard tests/*.c)
TEST_BIN := build/test_app
.PHONY: all clean test format lint coverage
all: $(BIN)
build/%.o: src/%.c | build
$(CC) $(CFLAGS) -c $< -o $@
$(BIN): $(OBJ) | build
$(CC) $(OBJ) -o $@ $(LDFLAGS)
build:
mkdir -p build
clean:
rm -rf build
# 简单测试:把 tests/*.c 和 src 需要的对象一起链接
test: $(OBJ) | build
$(CC) $(CFLAGS) $(TEST_SRC) $(OBJ) -o $(TEST_BIN) $(LDFLAGS)
$(TEST_BIN)
# 代码格式:需要安装 clang-format
format:
clang-format -i $(SRC) $(TEST_SRC) $(wildcard include/*.h)
# 静态检查:需要安装 cppcheck
lint:
cppcheck --enable=all --inconclusive --std=c11 --inline-suppr \
-Iinclude src tests
# 覆盖率(可选):需要 gcov/lcov
coverage: CFLAGS += --coverage
coverage: LDFLAGS += --coverage
coverage: clean test
lcov --capture --directory . --output-file build/coverage.info
genhtml build/coverage.info --output-directory build/coverage_html
.gitlab-ci.ymlMR 必须通过构建与测试;
main上额外打包制品。
yaml展开代码stages: [build, test, package]
variables:
GIT_STRATEGY: fetch
build:
stage: build
image: gcc:13
script:
- make clean
- make all
artifacts:
paths: [ "build/app" ]
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH == "develop"'
test:
stage: test
image: gcc:13
script:
- make test
needs: [build]
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH == "develop"'
package:
stage: package
image: alpine:3.20
script:
- mkdir -p dist && cp build/app dist/
- tar -czf dist/app-${CI_COMMIT_SHORT_SHA}.tar.gz dist/app
artifacts:
paths: [ "dist/app-*.tar.gz" ]
expire_in: 7 days
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
yaml展开代码stages: [format, lint, build, test, coverage, package]
format:
stage: format
image: alpine:3.20
before_script:
- apk add --no-cache clang-extra-tools
script:
- make format
# 若要强制格式一致,可开启:
# git diff --exit-code || (echo "Run make format"; exit 1)
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
lint:
stage: lint
image: alpine:3.20
before_script:
- apk add --no-cache cppcheck
script:
- make lint
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
build:
stage: build
image: gcc:13
script:
- make clean
- make all
artifacts:
paths: [ "build/app" ]
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH == "develop"'
test:
stage: test
image: gcc:13
script:
- make test
needs: [build]
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH == "develop"'
coverage:
stage: coverage
image: alpine:3.20
before_script:
- apk add --no-cache lcov
script:
- make coverage
- lcov --list build/coverage.info | tail -n +1
artifacts:
when: always
paths: [ "build/coverage.info", "build/coverage_html" ]
reports:
coverage_report:
coverage_format: cobertura
path: build/coverage.info
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH == "develop"'
package:
stage: package
image: alpine:3.20
script:
- mkdir -p dist && cp build/app dist/
- tar -czf dist/app-${CI_COMMIT_SHORT_SHA}.tar.gz dist/app
artifacts:
paths: [ "dist/app-*.tar.gz" ]
expire_in: 14 days
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
若仅在受保护分支允许部署密钥,请将发布 Job 限制在
main并相应设置变量为 Protected。
文件:.gitlab/merge_request_templates/默认.md
md展开代码### 变更内容
-
### 关联 Issue
- Closes #
### 影响面与风险
- 接口/ABI/配置变更?
- 性能/内存占用变化?
### 校验清单
- [ ] 本地 `make test` 通过
- [ ] clang-format 已执行
- [ ] CI build/test/coverage 通过
- [ ] 评审意见已处理
bash展开代码# 确保 develop → main 的 MR 已合并且 CI 全绿
git checkout main
git pull
# 打标签
git tag -a v1.2.0 -m "release: v1.2.0"
git push origin v1.2.0
# 在 GitLab Releases 页面基于 v1.2.0 生成说明并附制品
回滚:优先 git revert 生成反向提交;避免修改公共分支历史。
.git/hooks/pre-commit(记得 chmod +x .git/hooks/pre-commit)
bash展开代码#!/usr/bin/env bash
set -e
echo "[pre-commit] clang-format & cppcheck & unit tests"
make format
make lint
make -s test
bash展开代码git config --global user.name "你的名字"
git config --global user.email "you@your.org"
git config --global core.editor "vim" # 或 code --wait
git config --global init.defaultBranch main
状态/差异/历史
bash展开代码git status
git diff
git diff --staged
git log --oneline --graph --decorate --all
git show <commit>
git blame path/to/file.c
选择性暂存与提交
bash展开代码git add -p # 按块选择更干净
git commit -m "feat(parser): add tokenizer with error codes"
git commit --amend # 修正上一提交(未共享时)
撤销暂存/工作区修改
bash展开代码git restore --staged src/a.c
git restore src/a.c # 丢弃工作区改动(危险)
忽略文件
gitignore展开代码/build/ *.o *.a *.so
bash展开代码git rm -r --cached build
git commit -m "chore: apply .gitignore to build artifacts"
新建/切换/删除
bash展开代码git switch -c feature/log-rotate git switch main git branch -d feature/log-rotate git branch -D feature/log-rotate
远端同步
bash展开代码git remote -v git fetch origin git pull --rebase git push -u origin feature/x git push --force-with-lease
合并
bash展开代码git switch develop git merge feature/x git merge --no-ff feature/x
变基
bash展开代码git switch feature/x
git fetch origin
git rebase origin/develop
# 冲突:修复 -> git add -> git rebase --continue
挑拣提交
bash展开代码git switch release/1.2 git cherry-pick <commitA> <commitB>
bash展开代码git stash push -m "WIP: refactor io layer"
git stash list
git stash show -p stash@{0}
git stash pop
git stash apply stash@{1}
bash展开代码git tag -a v1.2.0 -m "release: v1.2.0"
git push origin v1.2.0
git show v1.2.0
典型冲突标记
text展开代码<<<<<<< HEAD int parse(const char* s) { return 0; } ======= int parse(const char* s) { return errno; } >>>>>>> feature/errno
A:在 merge 中解决冲突
bash展开代码git switch develop
git merge feature/errno
# 冲突产生
git status
# 编辑冲突文件 ->
git add src/parser.c
git commit # 或 git merge --continue
B:在 rebase 中解决冲突
bash展开代码git switch feature/errno
git rebase origin/develop
# 冲突
git status
# 编辑 -> git add 冲突文件
git rebase --continue
# 放弃变基:
git rebase --abort
辅助
bash展开代码git diff --merge
git checkout --theirs src/a.c # 选对方版本(冲突状态)
git checkout --ours src/a.c # 选本方版本(冲突状态)
层 1:工作区/索引
bash展开代码git restore path/to/file.c git restore --staged path/to/file.c
层 2:撤销最近提交但保留改动
bash展开代码git reset --soft HEAD~1
git reset HEAD~1 # mixed(默认):改动回到工作区
层 3:生成反向提交(安全,公共分支推荐)
bash展开代码git revert <bad-commit> git revert <A>..<B> git revert -m 1 <merge-commit>
层 4:硬重置(破坏性,仅私有分支)
bash展开代码git reset --hard HEAD~2 git push --force-with-lease
后悔药:reflog 恢复
bash展开代码git reflog
git reset --hard <sha>
# 或先拉个分支保存:
git switch -c rescued <sha>
误提交敏感文件
bash展开代码git rm --cached secrets.txt
echo "secrets.txt" >> .gitignore
git commit -m "security: remove secrets from repo and ignore"
# 立刻轮换密钥;如需历史清理,评估后使用 filter-repo 并与团队确认
历史整理(交互式变基)
bash展开代码git rebase -i HEAD~5
# pick -> squash/fixup 清理 WIP
审查前自检
bash展开代码make -s all && make -s test
clang-format -i $(git ls-files '*.c' '*.h')
git diff --check
git diff origin/develop...HEAD
git log origin/develop..HEAD --oneline
速查清单
git statusgit diff / git diff --stagedgit add -p → git commit -m "…"git commit --amendgit switch <name> / git switch -c <name>git fetch → git pull --rebase → git pushgit merge --no-ff feature/xgit rebase origin/developgit stash push -m "…" / git stash popgit revert <commit>git reset --soft/mixed/hard <ref>git refloggit tag -a vX.Y.Z -m "…" && git push origin vX.Y.Zgit checkout 用法速查与对照Git ≥2.23 把“切分支”和“还原文件”拆成了
git switch/git restore。很多团队仍在用git checkout,这里给出 两套写法对照。
bash展开代码git checkout develop # 切换到已存在分支
git switch develop
git checkout -b feature/parser-refactor # 新建并切换
git switch -c feature/parser-refactor
git checkout - # 回到上一个分支
git switch -
bash展开代码git checkout -t origin/develop
git switch -c develop --track origin/develop
git checkout --detach origin/main # 分离 HEAD 查看远端 main
# 返回:git checkout develop
bash展开代码git checkout -- src/parser.c # 还原到索引版本
git restore src/parser.c
git checkout HEAD -- src/parser.c # 还原到 HEAD
git restore --source=HEAD src/parser.c
git checkout main -- include/parser.h # 从其他分支取文件
git checkout 3f2a7c9 -- src/io.c
git restore --source=main -- include/parser.h
git restore --source=3f2a7c9 -- src/io.c
TIP: 加
--避免与分支名混淆。git checkout -- .会把所有已跟踪文件恢复到索引(谨慎)。
bash展开代码git checkout --theirs src/parser.c
git checkout --ours src/parser.c
git add src/parser.c
# merge:
git merge --continue
# rebase:
git rebase --continue
语义:merge 时 ours=当前分支、theirs=被合并分支;rebase 时 ours=你的提交、theirs=目标基线。
bash展开代码git checkout 3f2a7c9 # 暂时回到历史(分离 HEAD)
git checkout -b hotfix/from-3f2a7c9 # 拉分支保存
git reflog
git checkout -b rescue @{-1} # 基于上一次位置开分支
git checkout 融入你的日常脚本bash展开代码git checkout main
git pull origin main
git checkout -b feature/parser-refactor
git add src/parser.c include/parser.h
git commit -m "feat(parser): refactor tokenizer and error handling"
git push -u origin feature/parser-refactor
# 在 GitLab 发起 MR(target=develop)
bash展开代码git fetch origin
git checkout feature/parser-refactor
git rebase origin/develop
# 冲突:可用 git checkout --ours/--theirs 定向取版本
git add <冲突文件>
git rebase --continue
git push --force-with-lease
bash展开代码git checkout main -- include/common.h
git commit -m "chore: sync common.h from main"
bash展开代码git checkout -- src/a.c # 撤销工作区某文件改动
git reset --soft HEAD~1 # 撤销最近提交但保留改动
git revert <bad-commit> # 公共分支安全回退
git checkout -b backup-before-hard-reset
git reset --hard HEAD~2
--:git checkout foo 可能被当成切分支;还原文件请写 git checkout -- foo。git checkout .:会把所有已跟踪文件恢复到索引;若索引不正确,会覆盖你的正确改动。amend/rebase/reset --hard 仅限私有分支;公共分支用 revert。本文作者:冬月
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!