版本管理

Git

私有服务搭建Gitea

仓库信息读取

1. commit统计

# 读取仓库贡献者和对应的commit数
# -s (--summary):只显示提交总数,不列出具体的 commit 信息。
# -n (--numbered):按照提交数量从多到少排序。
git shortlog -sn

Commit规范

Commit 信息的基本结构

一个规范的 Commit 信息通常包括以下部分:

  • 类型(Type):说明 Commit 的类型,例如 featfixdocs 等。
  • 范围(Scope):可选,说明 Commit 影响的范围,例如模块、组件或文件。
  • 主题(Subject):简洁地描述 Commit 的目的,不超过 50 个字符。
  • 正文(Body):可选,详细描述 Commit 的内容和动机。
  • 脚注(Footer):可选,用于关联 Issue 或 Breaking Changes。
<类型>(<范围>): <主题>

<正文>

<脚注>

以下是常见的 Commit 类型:

类型描述
feat新增功能(Feature)。
fix修复 Bug。
docs文档更新(Documentation)。
style代码格式调整(不影响代码逻辑,如空格、缩进等)。
refactor代码重构(既不修复 Bug 也不新增功能)。
perf性能优化(Performance)。
test测试代码(新增或修改测试用例)。
chore构建过程或辅助工具的变动(如依赖更新、配置文件修改等)。
ci持续集成(Continuous Integration)相关的修改。
revert回滚之前的 Commit。
build构建系统或外部依赖的修改(如 Webpack、Gulp 等)。
merge合并分支。

Commit 信息的基本结构

feat: 新增用户登录功能
fix(用户模块): 修复登录时密码验证失败的问题

在用户登录时,密码验证逻辑存在错误,导致部分用户无法登录。此 Commit 修复了该问题。
eat(订单模块): 新增订单取消功能

BREAKING CHANGE: 订单取消接口的返回值结构发生变化,请前端同事注意更新。

配置用户和Email

配置文件通常位于~/.gitconfig中。

# 配置
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
# 获取
git config --global --get user.email

常规操作

删除远程分支

git push origin --delete feature-branch

仅更新远程信息

# 这个命令不会将远程分支拉下来,只会更新remote信息。
git fetch --all/[remote name]

# 来更新远程引用并删除已删除的远程分支的引用
git fetch --prune

# 只拉取一层
git fetch --depth=1 origin <branch>

停止跟踪文件

如果你想停止跟踪文件example.txt,可以运行,这样会停止跟踪这个文件并且不会删除这个文件。

git rm --cached example.txt

获取信息

# 获取简短的版本号
git rev-parse --short HEAD

# 获取一行详细信息
git log --pretty='format:[%ci][%h] %s' -1

# 在未同步submodule的情况下获取commit id
git ls-files --stage data | awk '{print $2}'

tag

# 查看所有标签
git tag

# 查看标签详情
git tag v1.0.0

# 创建附注标签
git tag -a <tag_name> -m "message"

# 推送tags
git push origin --tags

# 推送特定tag:eg v1.0.0
git push origin v1.0.0

# 同步远端tags
git fetch --tags
# 同步且删除本地 远端没有的tag
git fetch --prune origin "+refs/tags/*:refs/tags/*"

# 删除远端tag
git push origin --delete <tag-name>

记住密码

使用cache

# linux下配置上超时记录密码,可以避免多次输入密码
git config --global credential.helper 'cache --timeout=3600'

使用GCM(git credential manager)

配置文档见: https://github.com/git-ecosystem/git-credential-manager/blob/main/docs/credstores.md

# 从https://github.com/git-ecosystem/git-credential-manager/releases 下载对应的包

# 通常使用deb进行安装
sudo dpkg -i name.deb

git-credential-manager configure

# 安装pass
sudo apt install pass

# 生成gpg pair
gpg --gen-key

# 查看gpg-id
gpg -k

# init passl
pass init <gpg-id>

# 更新git credential 使用gpg
git config --global credential.credentialStore gpg

# 配置信息都可以在~/.gitconfig中看到

# gpg的相关配置

# 重启gpg,重新认证
gpgconf --kill gpg-agent
gpgconf --launch gpg-agent

# 尝试解码
gpg --decrypt /home/faceunity/.password-store/git/https/yourhost.com/kaihang.gpg

# 如果遇到ssh -Y 情况下 git pull调用gpg出错,错误如下,这时候是gpg没有正确找到tty,需要配置export GPG_TTY=$(tty)
> git pull 
fatal: Failed to decrypt file '/home/faceunity/.password-store/git/https/yourhost.com/kaihang.gpg' with gpg. exit=2, out=, err=gpg: encrypted with 3072-bit RSA key, ID 857FF0A72B89406E, created 2025-01-16
      "kaihang <kaihangy@outlook.com>"
gpg: public key decryption failed: Inappropriate ioctl for device
gpg: decryption failed: No secret key

Username for 'https://yourhost.com':

# 修改密码之后

使用libsecret

# linux
# Install dependencies
sudo apt install make gcc git libsecret-1-0 libsecret-1-dev libglib2.0-dev

# Compile binary
sudo make --directory=/usr/share/doc/git/contrib/credential/libsecret

# Configure git to use binary as credential storage
git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret

# 查看
cat ~/.gitconfig

打包commit

方法: git format-patch 和 git am(保留提交历史) 适用场景: 你希望在目标工程中保留完整的 Git 提交记录(Commit Log、作者、时间)。这是 Git 标准的协作方式。

打包(导出 Patch)

在源仓库中,使用 format-patch。它会为每一个 commit 生成一个单独的 .patch 文件。

  • 打包两个 Commit 之间的所有变更(区间):

        # 语法:git format-patch <起始Commit>..<结束Commit>
        # 注意:生成的 patch 不包含起始Commit本身,包含结束Commit
        git format-patch 8a1b2c..9x8y7z

    这会在当前目录下生成类似于 0001-xxx.patch, 0002-xxx.patch 的文件。

  • 打包单个 Commit:

    git format-patch -1 <CommitHash>
  • 技巧:把所有 patch 打包到一个指定文件夹:

    git format-patch 8a1b2c..9x8y7z -o ./my_patches

应用(导入 Patch)

将生成的 patch 文件(或文件夹)复制到目标工程,然后使用 git am (apply mailbox) 命令。

  • 应用单个文件:

    git am 0001-fix-login-bug.patch
  • 应用文件夹下的所有补丁(按顺序):

    # 其中-3 表示使用 3-way Merge(智能合并)。
    # 默认的 git am 是“傻瓜式”覆盖,而加上 -3 参数后,
    # Git 会尝试寻找两个仓库共同的祖先,像合并分支一样进行智能合并,
    # 能自动解决大部分行号偏差或轻微的上下文不一致问题。
    git am -3 my_patches/*.patch

submodule

增加新的submodule

git submodule add repoid your_directory

修改submodule的remote

# 先修改.gitmodules中需要修改的模块的url

# 更新子模块配置
git submodule sync

# 更新子模块的remote url
git submodule update --init --recursive

删除submodule

  • 步骤 1: 删除 submodule 的引用
git submodule deinit -f path/to/submodule

这个命令会从 .git/config 文件中删除 submodule 的引用。你也可以手动编辑 .git/config 文件删除相应的 submodule 配置。

  • 步骤 2: 删除 submodule 文件
git rm -f path/to/submodule
# 该命令,git version 2.25.1 会同步删除.gitmodules的对应条目

这个命令会从 Git 仓库中删除 submodule 所在的目录

  • 步骤 3: 更新 .gitmodules 文件
vim .gitmodules

或者使用你喜欢的编辑器打开 .gitmodules 文件,然后删除与 submodule 相关的条目。

多重remote下url的管理

# 假如当前仓库和submodule都有两个remote:origin、self
# 如何优雅的分别往两个仓库推,并且不冲突呢,问题主要在.gitmodules这个文件

# 这里假定以origin为主,首次的情况下
# submodule每个remote分别推送
git push origin master # 往origin推
git push self master # 往self推

# 当前仓库下
# 首先修改origin下的东西,并且推送
git push origin master

# 随后切换到remote self的master
# 注意这里是临时分支,修改完之后通过HEAD:remote_branch进行推送
git checkout self/master 

# 合并remote origin的修改
git merge origin/master
# 修改.gitmodules文件中submodule的url到remote self
...
# 随后执行以下指令更新submodule的链接
git submodule sync
git submodule update

# 随后commit,并且推送
...
git push self HEAD:master


# 同步self/master代码,注意不要pull
git fetch self

未同步情况下操作

# 在未同步submodule的情况下获取commit id
git ls-files --stage data | awk '{print $2}'

# 未同步的情况下更新submodule的commit id
git update-index --add --cacheinfo  mode,sha1,submodule_path
# he `mode` value can get from `git ls-files --stage submodule_path | awk '{print $1}'`

remote操作

# 查看remote地址
git remote get-url origin
# 修改remote地址
git remote set-url origin your_repo_address
# 删除remote
git remote remove origin

git lfs

# 初始化
git lfs install 
# 跟踪文件
git lfs track path_to_your_file

# 跟踪文件夹和其子文件夹
git lfs track your_dir/**

# 卸载git lfs
git lfs untrack your_dir/**

# 查看所有跟踪信息
git lfs track

# 查看跟踪的文件
git lfs ls-files

# 将lfs指针转换为普通文件
git lfs migrate export --include="your_dir/**"

# 删除本地git lfs数据
rm -rf .git/lfs

# 迁移仓库
# 首先在本地拉取所有的lfs数据
git lfs fetch --all
# 随后先推送lfs数据
git lfs push --all origin

网络相关

http代理

# 设置代理
git config --global http.proxy http://ip:port
git config --global https.proxy http://ip:port

# 获取代理
git config --global --get http.proxy
git config --global --get https.proxy

# 清空代理配置
git config --global --unset http.proxy
git config --global --unset https.proxy

ssh代理

# ~/.ssh/config
Host github.com
   HostName github.com
   User git
   # 走 HTTP 代理
   ProxyCommand socat - PROXY:192.168.0.52:%h:%p,proxyport=7890

查看当前ssh使用的key

ssh -T git@<your_git>

gitlab-runner

example

variables:
  GIT_CLEAN_FLAGS: -ffdx -e release-repo/

stages:
  - build
  - release
  - deploy

# build_linux
build_linux:
  stage: build # 任务阶段
  script:
    - ./scripts/ci/build/build_linux.sh -r # Default build release lib
  tags:
    - cuda
    - linux
    - fugan

# build_windows
build_win64:
  stage: build # 任务阶段
  script:
    - .\scripts\ci\build\build_win64.bat -r # Default build release lib
  tags:
    - cuda
    - windows
    - fugan

# Only build debug library
build_linux_debug_libs:
  stage: build
  script:
    - ./scripts/ci/build/build_linux.sh -d -i # build and archive debug libs.
  artifacts:
    paths:
      - ./release/**
    expire_in: 1 days
  when:
    manual
  tags:
    - cuda
    - linux
    - fugan

build_linux_tools:
  stage: build
  script:
    - ./scripts/ci/build/build_linux.sh -r -i -t # build and archieve tools with libs
  artifacts:
    paths:
      - ./release/**
    expire_in: 1 days
  when:
    manual
  tags:
    - cuda
    - linux
    - fugan

# Release scripts
release_linux:
  stage: release
  needs: [build_linux]
  script:
    - ./scripts/ci/release/release_linux.sh # release win64
  artifacts:
    paths:
      - ./release/**
    expire_in: 1 days
  tags:
    - cuda
    - linux
    - fugan
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+(-[a-zA-Z]+[0-9]*)?(\+.*)?$/'
      when: on_success
    - when: never

release_win64:
  stage: release
  needs: [build_win64]
  script:
    - .\scripts\ci\release\release_win64.bat # release win64
  artifacts:
    paths:
      - .\release\**
    expire_in: 1 days
  tags:
    - cuda
    - windows
    - fugan
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+(-[a-zA-Z]+[0-9]*)?(\+.*)?$/'
      when: on_success
    - when: never

deploy:
  stage: deploy
  needs: [release_linux, release_win64] # will download artifacts
  script:
    - ./scripts/ci/deploy/deploy.sh $CI_COMMIT_TAG
  tags:
    - deploy
    - linux
    - fugan
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+(-[a-zA-Z]+[0-9]*)?(\+.*)?$/'
      when: on_success
    - when: never

artifacts

artifacts是可以跨阶段和跨机器的,需要定义needs,之后这个需要的阶段会自动下载artifacts。

保持文件不删除

variables:
  GIT_CLEAN_FLAGS: -ffdx -e release-repo/

案例

优化中文不正常显示

# 如果git status中文显式数字的话,执行以下代码。
git config --global core.quotepath false

将 Commit 从 A 处迁移到 B 处

1. 导出变更(在源仓库)

A. 保留提交历史(推荐)

生成包含作者、时间、Commit 信息的文件,适合标准迁移。

# 打包最近的一次 commit
git format-patch -1 HEAD

# 打包指定区间 (不含 start, 含 end)
git format-patch <StartCommitHash>..<EndCommitHash>

# 打包到指定目录
git format-patch <Start>..<End> -o ./patches
B. 仅导出代码内容(无 Commit 记录)
git diff <Start> <End> > changes.patch

2. 应用变更(在目标仓库)

A. 标准应用(对应 format-patch)
# 应用单个或多个
git am *.patch
B. 仅应用代码(对应 diff)
git apply changes.patch

3. 遇到冲突时的解决方案(按严重程度排序)

当 git am 报错(如 patch failed)且流程暂停时,请按以下步骤处理:

方案一:开启 3-way Merge(最常用,推荐首选)

git am 默认很严格,开启 -3 可以让 Git 尝试智能合并(类似 git merge)。

# 1. 放弃当前失败状态
git am --abort

# 2. 重新应用,带上 -3 参数
git am -3 *.patch
  • 结果:如果成功则自动通过;如果有冲突,会产生 <<<< >>>> 标记,手动修完后 git add . 然后 git am --continue
方案二:忽略空白字符(针对缩进问题)

如果报错提示 whitespace 相关,或仅仅因为 Tab/Space 不一致:

git am --ignore-whitespace *.patch
方案三:强制手动修补(终极方案)

当方案一报错:It does not apply to blobs recorded in its index,说明两个仓库差异太大,Git 找不到合并基准。此时不要 abort,直接原地手动修。

  1. 强制打入补丁(Git 会把当前卡住的 patch 强行应用,无法应用的生成 .rej 文件):
    # .git/rebase-apply/patch 是 git am 正在处理的那个补丁文件
    git apply --reject .git/rebase-apply/patch
  2. 查看状态
    git status
    # 关注生成的 *.rej 文件
  3. 手动修复
    • 打开 .rej 文件查看丢失的代码片段。
    • 打开对应的源码文件,手动将代码粘贴进去。
    • 修复完成后,删除 .rej 文件。
  4. 提交修复并继续
    # 将修改好的文件放入暂存区
    git add .
    
    # 告诉 git am 继续处理下一个 patch (不需要 git commit)
    git am --continue

4. 常用控制命令

  • 放弃本次所有操作,回到原点git am --abort
  • 跳过当前这个报错的 Commitgit am --skip
  • 解决完冲突后继续git am --continue

SVN

私有SVN搭建SVN服务

命令

add

# 添加文件
svn add <filename:1> [<filename:2> ...]

# 添加文件夹和文件夹中的所有内容
svn add <dirname:1> --force

# 添加所有未版本控制的文件
svn add . --force

status

# ?: 表示文件、文件夹不在SVN版本控制中
# A: 表示文件、文件夹已被添加到版本控制
# M: 表示文件已修改
# !: 表示找不到文件夹,通常是文件系统删除掉了,但是没有通知svn,这种无法commit
# D: 表示使用svn remove删除了,可以commit了

checkout

# checkout repo 到某个local文件夹
svn checkout <repository_url> [local_directory]

update

# 将本地工作副本更新为SVN仓库中最新的版本
svn update [path] [-r revision]

revert

# 恢复文件
svn revert <path_to_your_file>

# 递归恢复文件夹
svn revert -R <path_to_your_dir>

commit

# 推送修改
svn commit -m [--message]