git详解

详细介绍git

使用方式基础参考

git一图流

image-20211207151102904

git详解

Git与svn对比

**SVN(Subversion)**是集中式版本控制系统,版本库是集中放在中央服务器的,而干活的时候,用的都是自己的电脑,所以首先要从中央服务器哪里得到最新的版本,然后干活,干完后,需要把自己做完的活推送到中央服务器。集中式版本控制系统是必须联网才能工作,如果在局域网还可以,带宽够大,速度够快,如果在互联网下,如果网速慢的话,就郁闷了。
下图就是标准的集中式版本控制工具管理方式:

image-20230103162133191

集中管理方式在一定程度上看到其他开发人员在干什么,而管理员也可以很轻松掌握每个人的开发权限。
但是相较于其优点而言,集中式版本控制工具缺点很明显:

  • 服务器单点故障
  • 容错性差

Git

Git是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样,工作的时候就不需要联网了,因为版本都是在自己的电脑上。既然每个人的电脑都有一个完整的版本库,那多个人如何协作呢?比如说自己在电脑上改了文件A,其他人也在电脑上改了文件A,这时,你们两之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
下图就是分布式版本控制工具管理方式:

image-20230103162249584

git工作流程

  1. 从远程仓库中克隆 Git 资源作为本地仓库。
  2. 从本地仓库中checkout代码然后进行代码修改
  3. 在提交前先将代码提交到暂存区。
  4. 提交修改。提交到本地仓库。本地仓库中保存修改的各个历史版本。
  5. 在修改完成后,需要和团队成员共享代码时,可以将代码push到远程仓库。

下图展示了 Git 的工作流程:

image-20230104113924564

Git安装

下载地址:https://git-scm.com/download

1
2
3
4
#winget安装方式
winget install --id Git.Git -e --source winget
#添加到系统路径
$env:PATH += ";C:\Program Files\Git\bin"

关键词理解

在初始化git版本库之后会生成一个隐藏的文件 .git ,可以将该文件理解为 git 的版本库 repository,而我们自己建立的项目文件夹即工作区 working directory , 在 .git 文件夹里面还有很多文件,其中有一个 index 文 件 就是暂存区也可以叫做 stage , git 还为我们自动生成了一个分支 master 以及指向该分支的指针head ,如下图

image-20230112111626807

  • 工作区: 存储项目文件的目录, 版本库需要创建到工作区中

  • 版本库: 创建出的隐藏目录 .git

    • stage - 暂存区
      当往工作区中添加了新文件之后, 需要将工作区文件添加到暂存区

    • master - 主分支

      默认只有这一个, 进行版本管理
      HEAD - 操作master分支的指针

    • 暂存区和分支的关系

      当暂存区的文件内容发送变化, 需要将其提交的master分支
      只有提交之后才会形成一个节点(一个版本)

可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。

使用git管理文件

使用git help git关键词可以查询git关键词的作用

创建版本库

什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。由于git是分布式版本管理工具,所以git在不需要联网的情况下也具有完整的版本管理能力。

在要使用git管理的目录中点击右键中选择Git Bash来启动,创建仓库执行命令:git init

版本库创建成功,会在此目录下创建一个.git的隐藏目录

  • 版本库.git目录就是版本库,将来文件都需要保存到版本库中。
  • 工作目录:包含.git目录的目录,也就是.git目录的上一级目录就是工作目录。只有工作目录中的文件才能保存到版本库中。

添加暂存区并提交

添加新文件到暂存区并提交

  1. 添加改动到暂存区 git add .(.表示所有变动,但不包含已删除的文件 git add -A才是真正的所有改动)
  2. 暂存区提交到分支 git commit -m '注释'

第一次使用git执行commit之前需要设定个人资料,点击跳转参考

还原修改

还原的本质 : 将工作区中修改的文件还原成想要的提交的版本

还原有三种情况

  • 只是修改了文件,没有任何 git 操作

    1
    2
    3
    git checkout -- aaa.html // 指定还原`aaa.html`文件

    git checkout -- * // 还原所有文件
  • 修改了文件,并提交到暂存区(即:编辑之后,进行git add 但没有 git commit -m "留言xxx"

    1
    2
    3
    4
    5
    git log --oneline            #只显示提交ID和提交信息的第一行,可省略

    git reset HEAD // 回退到当前版本

    git checkout -- aaa.html
  • 修改了文件,并提交到仓库区(即:编辑之后,进行git add 并且 git commit -m "留言xxx"

    1
    2
    3
    4
    5
    git log --oneline    #只显示提交ID和提交信息的第一行,可省略

    git reset HEAD^ // 回退到上一个版本,注意看HEAD后面有个^ HEAD^是回退到上个版本 HEAD^^是回退到上上个版本HEAD~数字 是回退到数字个版本

    git checkout -- aaa.html

前两种情况使用了还原功能后,修改内容就丢失了,无法找回.原因是真正保存下来的其实是每次提交的状态

另外有一种更常用的还原:

git restore 文件名 : 撤消工作区的修改返回到最近一次add(缓存区)的版本或者最近一次commit(当前版本库)的版本

git restore --stage <file>git restore <file>两个命令的区别

  • 对于git restore <file>命令,会撤销文件的修改,使文件恢复到暂存区或当前版本库(取决于文件在修改前的状态);
  • 对于git restore --staged <file>命令,把文件从暂存区撤回到工作区,保留文件最后一次修改的内容;

查看相关信息

  • git log 查看版本库中提交的各个节点信息
  • git status 查看当前状态(修改了什么,追踪了什么(暂存)还未提交),还可以查看是否同步了远程仓库的最新内容

对比修改内容

git diff的重点

文件的流转方向是由工作区创建,add进入暂存区,commit进入仓库。

  • git diff 比较暂存区与工作区文件的区别。
  • git diff --cached 比较仓库(版本库)与暂存区文件的区别。

对比未暂存修改

此时比较的是: 已暂存(staged)和 已追踪未暂存(modified) 之间的修改部分。
此时执行 git status 查看文件状态,git diff,显示内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#此时 hello.txt 中已经有一行内容了,内容为hello world,我们将其改为了hello
$ git status
On branch master
No commits yet

Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitignore
new file: hello.txt

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: hello.txt


$ git diff
diff --git a/hello.txt b/hello.txt
index 95d09f2..b6fc4c6 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1 @@
-hello world
No newline at end of file
+hello
No newline at end of file
#文件名后面 + 和 - 的数量是这个提交造成的更改中增删的项

对比已暂存修改

此时比较的是: 提交至仓库的版本 和 暂存区文件 之间的修改部分

git diff --cached 命令

注意:--staged--cached 是同义词
git diff --cached 等于 git diff --staged

1
2
3
4
5
6
7
8
9
10
11
12
13
#hello.txt 的内容为 hello。此时我们修改内容为 hi
$ git add hello.txt

$ git diff --cached
diff --git a/hello.txt b/hello.txt
index b6fc4c6..32f95c0 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1 @@
-hello
No newline at end of file
+hi
No newline at end of file

对比本地与远程仓库的区别

git diff 本地仓库名 远程仓库名/远程分支名 (远程与本地顺序可颠倒)

如: git diff master origin/master

添加忽略列表

有些文件或文件夹不需要git追踪,因此可以将他们添加到忽略列表

一般库文件和可执行文件,不需要git追踪

需要在项目根目录新建**.gitignore**文件(也就是与.git文件夹同级)
文件中写上忽悠的文件或文件夹,例如:

1
2
3
4
5
.vs
npoi.fast/bin
npoi.fast/obj
npoi.fast.test/bin
npoi.fast.test/obj

如果要忽略的文件或文件夹已提交过,请使用命令git rm -r --cached filename (其中filename换成文件或文件夹名,如上述.vsnpoi.fast/bin)

已经添加至 git 仓库的文件(commit 后的),是不能被 .gitignore 文件所影响的,需要先 git rm --cached 让其脱离 git 仓库。git rm --cached 后还需要commit

删除相关

git rm 等同于 rm + git add

git rm --cached会保留原文件,只删除对文件的追踪状态,常用于已提交后添加忽略的情况

与远程仓库交互

在线代码托管平台: Github 码云 等等

image-20211207131020235

本地同步到远程

这里指的是本地和远程的分支在过去节点就有差异的合并

下面介绍了两种本地同步到远程的方式,一种会产生合并分支;一种不会产生合并分支,即可以保持线性.

本地库上传到远程库参考

不产生合并分支的方式

[注意] 上传到远程库最稳妥的方式是push之前先pull

如果本地和远端本身就有不同,则可以通过git pull --rebase <远程主机名> <远程分支名> 将远程的数据拉到本地自动产生一个本地节点(是作为原本节点之前的节点(而不是最新的节点),符合rebase的语义),本地最新的节点此时既有原本远程节点的数据,也有改动数据,然后就可以正常push提交了

更流程化的做法如下:

  1. 当在使用 git pull --rebase 命令时,如果发生冲突,需要手动解决冲突并继续rebase操作。以下是解决冲突的一般步骤:
  2. 运行 git pull --rebase 命令时,如果发生冲突,Git会停止rebase操作并提示冲突的文件。
  3. 打开冲突的文件,手动解决冲突。在冲突标记(<<<<<<<,=======,>>>>>>>)之间编辑文件,将冲突部分修改为期望的内容。
  4. 保存文件后,运行 git add -u ,命令将解决冲突后的文件标记为已解决。
  5. 运行 git rebase --continue 命令继续rebase操作。Git会继续应用之前的提交,直到完成rebase操作。
其他方式

如果不使用 git pull --rebase ,而本地分支和远程分支有差异,您可以通过以下步骤提交代码:

  1. 首先,使用 git pull 命令拉取远程分支的最新代码到本地。这将自动合并远程分支的代码到本地分支,如果有冲突需要手动解决。
  2. 解决可能出现的冲突。在解决完冲突后,使用 git add 命令将解决冲突的文件标记为已解决。
  3. 运行 git commit 命令提交解决冲突后的代码。
  4. 如果需要将本地提交推送到远程分支,可以使用 git push 命令将本地提交推送到远程分支。 通过以上步骤,您可以在本地和远程分支有差异的情况下,手动合并代码并提交到远程分支。

请注意,这种方式可能会产生合并提交,如果希望避免产生合并提交,可以考虑使用 git pull --rebase 或其他方法来保持提交历史的线性和清晰。

远程同步到本地

远程库下载到本地库: git clone ssh链接或https链接

远程库更新到本地库: git pull <远程主机名> <远程分支名>:<本地分支名> (将<远程主机名><远程分支名>分支拉取过来,与<本地分支名>分支合并)

git pull 等同于 git fetch + git merge (git fetch是把远程仓库的内容下载到本地作为一个本地的子分支)

克隆clone和拉取pull区别

  • 本地仓库还没有的时候使用克隆
  • 本地仓库已经存在了,从远程仓库下载文件使用拉取

fetch,pull,clone详解点击跳转

  • fetch使用格式: git fetch <repositoryUrl>
  • merge使用格式: git merge <需要合并到当前分支的目标分支名>

推送本地仓库文件到远程仓库注意

  • 如果远程仓库没有任何分支,可以将本地仓库直接推送到远程仓库,不会报错;

  • 若远程仓库有master分支,则本地仓库推送master分支的时候会报错

    解决方法:首先优先将远程仓库的文件”获取”(fetch)到本地仓库,将自动在本地仓库建立了默认名字为**FETCH_HEAD**的子分支,然后将该子分支合并到master主分支(可能需要解决冲突),最后再执行推送操作(push)就可以了

注意: fetch指令生成的FETCH_HEAD子分支无法用git branch查看到

rebase指令详解跳转 (不推荐使用)

SSH协议配置

除了https协议连接,还可以使用ssh协议进行连接

加密的方式是非对称加密,需要生成一对密钥

  • 客户端拿私钥
  • github服务器拿公钥

生成SSH协议密钥对

生产秘钥对的命令: ssh-keygen -t rsa (rsa为非对称加密算法)

也可以ssh-keygen -t rsa -C "这里输入生成的sshkey的名称"

将生成id_rsa(私钥)和id_ras.pub(公钥)

公钥添加到 github 账户

github中设置公钥: 点击头像-Settings-SSH and GPG keys-New SSH keys中起个题目并且填入id_ras.pub中的key,最后点击Add SSH key

SSH协议私钥设置到本地

1
2
ssh-agent bash
ssh-add ~/.ssh/id_rsa # 这里如果文件名被改过要写你自己定义的文件名 (~/.ssh/id_rsa为私钥文件路径)

返回如下结果表示设置成功:

1
Identity added: id_rsa (your_email@example.com)

测试SSH协议连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#输入:
#github
ssh -T git@github.com
#码云
ssh -T git@gitee.com
#返回:
The authenticity of host 'github.com (20.205.243.166)' can't be established.
ECDSA key fingerprint is SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
#输入:
yse
#返回:
Warning: Permanently added 'github.com,20.205.243.166' (ECDSA) to the list of known hosts.
Hi xxxxxx You've successfully authenticated, but GitHub does not provide shell access.
#如果 xxxxxx 是github用户名,表示ssh链接成功

配置 git的push&pull 使用 SSH协议 连接

在仓库中输入git remote -v查看当前使用的是什么协议,如果以git开头表示SSH协议,下面是使用https协议返回的结果:

1
2
origin  https://xxxxx.git (fetch)
origin https://xxxxx.git (push)

输入 git remote set-url origin git@github.com:xxxxx.git 修改https协议为SSH协议,git@github.com:xxxxx.git取自github库

接下来使用push&pull就是使用的SSH协议

分支操作

git中默认只有一个分支:master

如果创建了分支,各个分支都是独立的,互不影响的

image-20230130173124159

当前所在的分支,其实是由 HEAD 决定的

命令名称 作用
git branch 分支名 创建本地分支
git branch 查看本地有哪些分支
git branch -d 分支名 删除本地分支
git push <主机名> -d <分支名> 删除远程分支,主机名不填默认是origin
git branch -v 查看本地分支+上次提交的信息
git branch -vv 查看本地分支+上次提交的信息+本地和远程分支的关系
git branch -vv -a 查看本地分支+上次提交的信息+本地和远程分支的关系+远程分支(如果不想显示提交的信息,也可以去掉-vv参数)
git branch -r 只查看远程分支
git checkout 分支名 切换本地分支
git checkout -b 分支名 创建本地分支并切换
git merge 分支名 把指定的分支合并到当前分支上
git branch -m 旧分支名 新分支名 修改本地分支名称
git merge --abort 回到解决合并冲突之前的状态
git branch -f 分支名1 分支名2 更新分支1以指向分支2
git branch -B 分支名1 分支名2 更新分支1以指向分支2并且切换到分支1
解决冲突

跳转参考解决冲突具体代码操作

合并中有冲突的数据会显示为如下格式:

1
2
3
4
5
<<<<<<< HEAD
hello, git!!! master test!
=======
hello, git!!!hot-fix test!
>>>>>>> hot-fix

上面的HEAD分支的内容和下面hot-fix分支的内容,该处有冲突,需要手动解决冲突后再提交

git常用指令

常用环境设定

1
2
3
4
5
6
7
//编辑器更换:
git config --global core.editor "code --wait"
//git缩写:
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.st status
git config --global alias.ci commit

查看所有config的设定

  • Mac:~/.gitconfig
  • Win:C:\Users\$USER

git版本查看

1
git --version

设定个人资料

1
2
3
4
5
6
//输入姓名
git config --global user.name "gon"
//输入个人的email
git config --global user.email "gonsakon@gmail.com"
//查询git设定内容
git config --list

基本指令架构

image-20211207131020235

上传指令

  1. 初始化git数据库:git init

    文件夹中多出一个.git文件夹,表明该文件夹已经生成了git数据库了(需要显示隐藏文件才能显示出来)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //macOS下打开隐藏文件显示
    defaults write com.apple.finder AppleShowAllFiles TRUE
    // 默认显示苹果所有文件 true
    killall Finder// 这一步相当于确定
    //关闭隐藏文件显示
    defaults write com.apple.finder AppleShowAllFiles FALSE
    killall Finder // 这样就恢复成初始状态了

    //也可以在访答中键入command+shift+.
  2. 关联远程库: git remote add origin https://github.com/your-username/my-repo.git命令将本地仓库与远程GitHub仓库关联起来。将your-username替换为你的GitHub用户名。

  3. 查询当前状态:git status

  4. 将有修改的档案加入到索引(暂存区):git add . (但不包含删除改动, git add -A才是真正的所有改动)

  5. 将索引档案变成一个更新(COMMIT):git commit -m "修改内容的描述"

  6. 将最新的更新修改为当前索引档案(这个命令会将你的更改添加到最新的提交中,而不是创建一个新的提交): git commit --amend -m "要修改的修改内容的描述"(不加-m加描述的部分则会打开文本编辑器,让你编辑上一次提交的描述)

  7. 观察commit历史记录:git log

  8. 下载远程数据库:git clone 数据库网址

  9. 更新远程数据库:git push origin master (此处的master为远程仓库的分支名)

1
2
3
4
5
6
7
//上传步骤
git add .//添加当前目录的所有文件到暂存区
git commit -m "修改内容的描述"
git push <远程主机名> <本地分支名>:<远程分支名>
//如果本地分支名与远程分支名相同,则可以省略冒号:
git push <远程主机名> <本地分支名>
git push --force <远程主机名> <本地分支名>:<远程分支名>//强制推送,不管任何事

[注意] 最好的方式是push之前先pull

远程储存库操作

  • 注册远程储存库origins名称对应远程储存库网址:git remote add origin 远程储存库网址(origin是自己取的远程储存库名字,怎么取都可以)
  • 更新资料到远程master分支:git push -u origin master(-u可以省略)
1
2
3
//上传到已经存在的远程储存库
git remote add 自己取的名字 远程数据库的git的url//注册远程储存库名称对应远程储存库网址
git push -u 自己取的名字 master(-u可以省略)

git版本控制

git版本细节

  • branch:分支,默认分支叫master
  • HEAD:当下的本地版本位置
  • origin:默认远程储存库
  • 回到某个版本内容:git checkout commit编号
  • 返回最新的版本:git checkout master(分支名称)
1
2
3
git log//查看当前所有的commit编号
git checkout 想回到的commit节点的哈希值//返回到指定哈希值的commit节点
//Git 对哈希的处理很智能。你只需要提供能够唯一标识提交记录节点的前几个字符即可

image-20211209200758510

commit编号就是个哈希值

image-20211209202104826

git checkout commit编号与直接在上图左侧双击commit行均为使得文件恢复到与commit时的样子,二者效果一致。

还原技巧

src=http___upload-images.jianshu.io_upload_images_4311354-6a4562939fec66c0.png&refer=http___upload-images.jianshu

Untracked:表示未跟踪的

未跟踪情况下

新建文件的时候,清空未跟踪的内容:

  • 显示此次清除的未跟踪内容:git clean -n
  • 强制清除未跟踪内容:git clean -f

还原工作目录上已更改的内容:

1
2
git checkout -- <file文件名>//将该文件已更改的内容还原
git checkout .//所有更改内容还原

已跟踪情况下

git reset 通过把分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。git reset 向上移动分支,原来指向的提交记录就跟从来没有提交过一样。

已经加入到暂存区的改动,退回到未加入缓存区状态:git reset HEAD

git reset head^^,两个^表示返回两个commit记录的节点转为未跟踪状态,多少^表示多少个commit记录节点。

git reset 很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的,取而代之的是git revert xxx

image-20211210151213508image-20211210151908657

Git分支(branch)

为什么要用分支?

  • 多人协作时,不可能都在master
  • 可以让master都是正式版资料,可以开分支来做测试或开发,让这些流程不影响正式主机分支
1
2
3
4
5
6
7
git branch 分支名//新建分支
git branch//查看当前有多少个分支
git checkout 分支名//切换到别的分支
git branch -d "分支名称"//删除分支,-D是强制删除
//如果你想创建一个新的分支同时切换到新创建的分支的话,可以通过 git checkout -b <your-branch-name> 来实现。
git merge 合并的分支名//提交新节点为合并分支,快转
git merge 要合并的分支名 --no-ff //合并分支,非快转

git checkout -B master详解:

这个命令会创建一个新的master分支,或者如果master分支已经存在,它会更新master分支到当前HEAD注意,如果master分支已经存在并且有未提交的更改,这个命令会覆盖那些更改。在运行这个命令之前,确保你的工作目录是干净的,或者你已经提交了所有的更改。如果你只是想创建一个新的分支指向当前提交,你可以使用git checkout -b new_branch_name。这将创建一个新的分支并检出到那个分支,而不会影响master分支。请根据你的需求选择合适的命令。

  • git checkout -b <branch>: 这个命令会创建一个新的分支,并且切换到这个新创建的分支。如果已经存在一个同名的分支,Git会返回一个错误。
  • git checkout -B <branch>: 这个命令和-b选项类似,但是如果存在一个同名的分支,它不会返回错误,而是将这个分支重置为当前HEAD。也就是说,如果分支已经存在,-B选项会将其重置为当前的HEAD,如果分支不存在,它会创建一个新的分支。

相对移动

  • 使用^向上移动1个提交记录
  • 使用~<num>向上移动多个提交记录,如~3

可以直接使用 -f 选项让分支指向另一个提交。例如:

1
git branch -f main HEAD~3

上面的命令会将 main 分支强制指向 HEAD 的第 3 级父提交。

git切换节点面临的问题

Git不允许切换分支时覆盖未提交的更改,以避免意外丢失工作

切换节点后,切换的节点又产生了改动,想要切换原节点会报错:下面三种方法解决:

  1. 提交更改:如果您希望保留当前分支上的更改,请先使用git add命令将更改添加到暂存区,然后使用git commit命令提交更改。提交后,您就可以使用git checkout 原节点切换到原节点了。

  2. 存储更改:如果您不想提交当前分支上的更改,而是暂时保存它们以便稍后使用,请使用git stash命令将更改存储起来。运行git stash将存储更改并将工作目录恢复到干净的状态,然后您就可以使用git checkout 原节点切换到原节点了。在需要恢复更改时,可以使用git stash apply命令将存储的更改重新应用到工作目录。

  3. 如果您不想保留当前分支上的更改,

    使用强制切换:您可以使用-f--force选项来强制切换分支,忽略未提交的更改。运行以下命令将当前分支切换到原节点,并丢弃未提交的更改:

    1
    git checkout -f 原节点

    请注意,这将不可逆地丢弃当前分支上的所有未提交更改,请确保您不再需要这些更改。

git标签

在Git中,标签(tag)是一种用于给特定提交打标签的机制,通常用于标记重要的版本或发布。标签可以帮助开发者快速识别和引用特定的提交历史。

推送标签的好处:

  1. 版本管理:标签通常用于标记软件的发布版本(例如v1.0、v2.0等),推送标签后,其他人可以轻松找到和下载这些特定版本。
  2. 协作:在团队合作中,推送标签可以确保所有成员都能看到相同的版本标记,避免版本混淆。
  3. 历史记录:标签提供了一个清晰的历史记录,帮助开发者理解项目的演变。

标签有两种类型:

  1. 轻量标签(Lightweight Tag):相当于一个指向特定提交的指针,类似于一个分支,但不随时间变化。创建轻量标签的命令是:
    git tag <tagname>
  2. 注释标签(Annotated Tag):包含更多信息,比如标签的作者、日期和附加注释。注释标签是推荐使用的,因为它们提供了更丰富的上下文。创建注释标签的命令是:
    git tag -a <tagname -m "your message">

当你创建标签时,Git会默认将标签指向当前的HEAD(即当前分支的最新提交)。例如,如果你在某个分支上创建标签,标签就会指向该分支的最新提交。

常用命令

  • 列出所有标签
    git tag

  • 查看特定标签的信息
    git show <tagname>

  • 列出所有标签及其指向的提交

    git tag -n

  • 推送标签到远程仓库:(查看标签所指向的具体提交信息,包括提交的哈希值、作者、日期以及提交信息)
    git push origin <tagname>

  • 推送所有标签到远程仓库
    git push origin --tags

标签在版本控制中非常有用,特别是在发布软件时,可以清晰地标记每个版本,方便团队成员和用户进行引用和下载。

git看这个就行

操作逻辑

多种git合并的区别

2941b8d0ac67870c6dbbedd607bf20d4

worktree详解

在高频率的任务切换场景下,Worktree 提供了真正的“并行开发”体验。它不同于 git clone,所有工作区共享同一个 .git 目录和历史记录,这意味着它极其节省空间,且同步远程更新非常快

Worktree 填补了 Stash(太轻量)和 Clone(太笨重)之间的空白

特性 Git Stash Git Worktree Git Clone
隔离性 低 (同一目录切换) 中 (不同目录并行) 高 (完全独立仓库)
存储开销 极低 低 (共享 .git) 高 (完整历史复用)
多任务能力 串行 (必须切分支) 并行 (多目录同时开) 并行 (完全独立)
适用场景 几分钟的临时中断 数小时/天的任务 并行长期、跨环境的完全隔离
远程同步 需先 unstash一次 Fetch 全局更新 每个克隆需独立 Pull

基本命令

  • 添加工作区 git worktree add [<options>] <path> [<branch>] 在指定路径创建一个新分支并检出

    • -b <new-branch>:创建并切换到新分支。
    • --detach:关联指定提交(分离 HEAD,不关联任何分支)。
    • --lock:锁定 Worktree(防止意外删除,Git 2.17+)。
  • 查看列表 git worktree list 列出当前仓库关联的所有工作区

  • 删除工作区 git worktree remove [-f] <path>

  • 清理已被删除但未使用remove指令移除的worktree元数据 git worktree prune

    如手动删除了 Worktree 目录但未运行 git worktree remove

注意事项

每个 Worktree 必须对应一个唯一的目录,且该目录不能与现有 Worktree(包括主仓库的工作目录)重叠(不能是父子目录关系)。例如:若主仓库目录为 /repo,则不能在 /repo/subdir/repo_parent/repo下创建 Worktree

git操作记录

如何让节点在分支间移动

git cherry-pick

git cherry-pick 是一个强大的 Git 命令,它允许你将一个或多个提交从一个分支选出(cherry-pick)并应用到你当前工作的分支上。这个命令非常有用,特别是在你想要将特定的更改从一个分支移植(或复制)到另一个分支,而不需要合并整个分支的历史时。

使用方式

基本的 git cherry-pick 命令格式如下:git cherry-pick <commit-hash>,其中 <commit-hash> 是你希望复制过来的提交的哈希值。你可以通过 git log 命令查看提交的哈希值。

多个提交

如果你想一次性应用多个提交,只需在命令后面列出所有想要应用的提交的哈希值,用空格隔开:git cherry-pick <commit-hash1> <commit-hash2> <commit-hash3>

<commit-hash1> 会首先被应用,然后是 <commit-hash2>,最后是 <commit-hash3>。Git 会依次将每个提交的更改应用到当前分支,并在目标分支上创建相应的新提交

也可以使用范围操作符用来指定一连串的提交,像这样:git cherry-pick <start-commit-hash>^..<end-commit-hash>

解决冲突

在 cherry-picking 过程中可能会遇到冲突。当发生冲突时,Git 会停止应用提交直到你解决冲突。解决冲突后,你需要通过以下命令继续:git cherry-pick --continue如果你决定不应用当前的 cherry-pick 操作,并想要取消它,可以使用:git cherry-pick --abort这将会停止 cherry-pick 操作,并将分支恢复到开始 cherry-pick 之前的状态。

选项

git cherry-pick 命令还有一些有用的选项,如:

  • --edit-e): 在提交前打开编辑器,允许你编辑提交信息。
  • --no-commit-n): 应用更改,但不产生新的提交。这允许你手动创建一个包含多个 cherry-pick 更改的单一提交。
  • --signoff-s): 在提交信息的末尾添加一个签名行,证明你是此次提交的作者。

示例

假设你有一个提交哈希值为 abcd123 的修复,你想将这个修复应用到当前分支上,你可以运行:git cherry-pick abcd123如果在应用过程中遇到合并冲突,解决冲突后,运行:git cherry-pick --continue这将提交更改,并将你的分支更新到包含新修复的状态。

git cherry-pick 是一个非常实用的工具,特别是在处理涉及多个分支的复杂项目时。正确使用可以在保持项目清晰度的同时,有效管理分支之间的具体更改。

不小心遗失节点

Git 提供了工具来帮助你恢复丢失的提交,你可以尝试以下方法

不知道丢失节点哈希值

  1. 使用 git reflog 查找丢失的提交:
    Git 的 reflog 记录了对于 HEAD 的所有移动,包括提交和 rebase 操作。你可以使用 git reflog 命令来查找丢失的提交。

  2. 在 reflog 输出中,查找失去的提交对应的 HEAD@{n} 引用。一旦找到它,你可以通过以下命令来恢复:

    git checkout -b 分支名 "HEAD@{n}"

    这将会创建一个新的分支并检出到丢失的提交状态

  3. 然后可以使用git cherry-pick将节点合并过来

  4. 再使用 git branch -d 分支名 删除第3步生成的临时分支

就成功将丢失的节点找回来合并到分支上了

知道丢失节点哈希值

  1. git checkout -b 分支名 节点哈希值

    这将会创建一个新的分支并检出到丢失的提交状态

  2. 然后可以使用git cherry-pick将节点合并过来

  3. 再使用 git branch -d 分支名 删除第1步生成的临时分支

合并本地的多个节点

往往在本地提交的多个节点信息,需要合并成一个节点作为一个相对完整的整体再推送到远程服务器上

以合并本地最新的两个节点为例,使用 git rebase 执行交互式变基(interactive rebase)。以下是操作步骤:

  1. git rebase -i HEAD~2

    这将会打开你的默认文本编辑器,并且显示最近的两个提交

    1
    2
    3
    pick 40e39bd 更新了曲线相似度相关代码,基本功能已实现完毕
    pick 62e4c9a 基本功能都已实现
    ...
  2. 将某个节点的pick改成squash,表示合并这个节点到前一个提交之中

    • pick 是默认的操作,意味着”选择”这个提交。当你将某个提交前面的命令设置为 pick,你告诉 Git 保留这次提交的更改和它的提交信息。基本上,你是在说,“就像我原来提交的那样保留这个更改”。
    • squash 操作会将当前提交与前一个提交合并。选择 squash 命令的效果是使当前提交的更改被合并到上一个使用 pick 命令的提交中,并且允许你整合这两个提交的提交信息。在 rebase 的过程中,如果你将一个提交标记为 squash,Git 会将它的更改与前一个提交合并,并在最后提示你修改最终的提交信息。(因此最靠前的节点不能是squash,因为之前没有pick的节点与其合并了,会报错)
  3. 保存并关闭编辑器。Git将会开始rebase过程,并且会打开另一个编辑器让你有机会重写合并提交的提交信息。

  4. 如果有冲突,会要求解决冲突,然后可以通过git rebase --continue继续rebase过程

至此完成

最新提交节点改名

git commit --amend

注意此改名,会将未提交修改带入节点中

撤销特定节点改动

在 Git 中,可以使用 git revert 命令来撤销某个特定提交(节点)所做出的改动。

git revert 会创建一个新的提交来抵消指定提交的影响,从而使项目状态回到该提交之前的样子。

新建项目的时候先修改.gitignore文件

对于那些之前已被追踪但现在想忽略的文件或目录,可以使用 git rm --cached <file/directory> 命令将其从版本库的追踪中移除,但保留本地文件(仅移除追踪状态)。

如果是需要递归处理目录,还得加-r

两个大项目之间切换防止互相影响

清理工作区:在切换到不需要该 dll 的分支之前,先清理工作区,确保没有残留的来自另一个分支的文件。可以使用 git clean -fdx(谨慎使用,确保不会误删重要文件)

以下是对 git clean -fdx 命令各部分的解释:

git clean:执行清理操作。

  • -f:表示强制(force),即强制进行清理,不进行额外提示。
  • -d:表示同时清理未跟踪的目录。
  • -x:表示连忽略文件(在 .gitignore 中指定的)也一起清理。

需要注意的是,这个命令会较为彻底地清理工作区,可能会误删一些你还需要的未跟踪文件,所以使用时要非常谨慎,最好先确保工作区没有重要的未跟踪文件。

取消最新提交节点,还原为修改

1
git reset --soft HEAD~1

还原一个节点

1
git reset --hard HEAD~1  #这个是丢弃更改

修改gitignore后让忽视内容真正从项目移除

需要使用命令git rm -r --cached <directory - name>,其中<directory - name>是要忽略的目录名称。例如,如果要忽略logs目录,可以运行git rm -r --cached logs。这个命令不会删除本地文件系统中的文件或目录,只是将它们从 Git 的缓存中移除,让.gitignore规则生效。

本地变动提交到远程的标准操作

1
2
git pull -r origin main  # 拉取远程 main 分支的更新,并进行 rebase
git push origin main # 将你的更改推送到远程 main 分支

含义git pull -rgit pull --rebase 的缩写。它的作用是从远程仓库获取最新的更改,并将这些更改应用到你当前的工作分支上。与普通的 git pull 不同,git pull -r 会使用 rebase 而不是 merge。(不会包含merge节点,而是先将改动拉过来,才应用自己的改动,提交历史会比较清晰)

工作原理:当你执行 git pull -r 时,Git 会先获取远程分支的更新,然后将你的本地提交“移动”到这些更新之后。这可以保持提交历史的线性,避免产生多余的合并提交。

强制重置回历史上任何一个提交点

当你执行 git pull遇到冲突,并且在解决过程中搞砸了,想要完全回到拉取之前的状态

核心思路: 利用 Git 的引用日志 (reflog) 找到 pull之前那个“干净”的提交点,然后强制重置回去。

最佳解决方案:使用 git reset --hard

这是最直接有效的方法,它会丢弃所有未提交的更改,包括你为解决冲突所做的所有尝试,让你的工作区完全回到拉取前的状态。

1
2
3
4
5
6
7
git reflog

#最近的操作历史会显示在最上面
HEAD@{0}: pull: Merge made by the 'ort' strategy.
HEAD@{1}: commit: My awesome work before pulling
HEAD@{2}: checkout: moving from feature-a to main
...

以回退到pull之前为例: 你需要找到 pull操作之前的那一行记录。通常是紧挨着 pull记录下面的那一行(比如上面的 HEAD@{1})。记下它的索引(如 HEAD@{1})或提交哈希值(如 abc1234

执行硬重置: 使用你在上一步中找到的索引或哈希值,运行 reset命令:

1
2
3
4
5
# 使用索引(推荐,更直观)
git reset --hard HEAD@{1}

# 或者使用具体的提交哈希值
git reset --hard abc1234

--hard选项会强制将你的工作目录和暂存区都重置到指定的提交点,就像什么都没发生过一样

行完这一步后,你的仓库就已经完美地回到了拉取之前的状态

同样有效的方法: 使用 git reset --hard ORIG_HEAD

Git 在进行危险操作(如合并)时,会将之前的 HEAD 位置保存在一个叫 ORIG_HEAD的临时引用中。这通常就是你拉取之前的状态。

1
git reset --hard ORIG_HEAD

这个命令的效果和上面基于 reflog的 reset 是一样的。

项目切换远程仓库流程

更详细的项目迁移到新的一个空的远程仓库流程可以参考[[运维#迁移流程|此处]]

1
2
3
4
5
6
#修改origin指向的链接
git remote remove origin
git remote add origin https://......
git remote remote -v #查看远程仓库
git fetch #提取远程仓库信息
git branch --set-upstream-to="origin/远程分支名" #使当前分支追踪指定的远程分支名

git合并vs两边解释

界面位置 通常代表 操作按钮 使用场景
左边 传入的更改 接受传入 你需要完全使用来源分支的代码时
右边 当前的更改 接受当前 你需要完全保留当前分支的代码时
下方结果窗口 最终合并结果 接受合并结果 (推荐) 你手动整合了双方代码后
  1. 不要盲目点“接受传入”或“接受当前”:这通常会丢失另一边的修改。这应该是你确认另一边的修改完全不需要时的最后手段。
  2. 手动编辑才是王道:花时间阅读左右两边的代码,理解为什么会产生冲突。然后,在结果窗格中直接编辑,创造出正确的、最终的代码。完成后点击“接受合并结果”。
  3. 理解上下文是关键:在解决冲突前,一定要知道你当前在哪个分支,正在合并哪个分支。问自己:“我这次操作的目标是什么?”
  4. 使用“比较”功能:它比主界面更直观,能帮你更好地做出决策。

git贮藏弹出的时候的冲突解决

重置回pop乾的状态使用指令: git reset --hard HEAD,会将代码还原回弹出前的情况,并且贮藏也会保留

git和github

  • Git:一个分散式版本控制软件,可以由它产生一个储存库(git Repository)
  • Github:支持git程序编程存取和远程托管储存库的平台服务

热门远程储存库

  • Github:拥有GitHube Pages功能,可拥有私人[[数据库]],免费方案是3人以下
  • Bitbucket:可拥有私人[[数据库]],免费方案是五人以下团队
  • GitLab:自架Git服务器,有提供web视觉化管理界面,常用语企业内部开发

github上贡献代码

  1. [先提一个issues(可选)],然后fork对方的项目

  2. 将自己fork的项目git clone下来

  3. 安装相关依赖,确保测试案例都通过

  4. 进行代码编写与修改

  5. git branch查看对方的分支

  6. git checkout -b 新分支的命名(新建一个新特性的新分支)

  7. git add -A 添加所有改动

  8. git commit -m “描述改动”

  9. git push –set-upstream origin 新分支的命名 (提交到远端的新分支)

    GitHub在2021年8月13日移除了对密码认证的支持。如果你在尝试推送到或从你创建的代码库中拉取,但认证失败,你可能会看到像git@github.com: Permission denied (publickey)Host key verification failed这样的错误。

    解决方案如下:

    1. 登录你的GitHub账户。点击右上角的个人资料图片,然后选择设置
    2. 在左侧的侧边栏中,点击开发者设置
    3. 在左侧的侧边栏中,点击个人访问令牌
    4. 点击生成新的令牌
    5. 令牌描述字段中,输入一个描述,例如“新的访问令牌”。
    6. 选择范围部分,选择你想要此令牌授予的权限。如果你不确定,你可以先选择所有的复选框。
    7. 点击页面底部的生成令牌
    8. 在下一个页面中,你会看到你的新令牌。复制此令牌,因为你将无法再次查看它。
    9. 现在当你尝试推送到你的仓库时,当Git提示你输入密码时,你应该输入你刚刚创建的个人访问令牌,而不是你的GitHub密码。
  10. 到fork的项目中,可以点击 Compare & pull request按钮

    image-20231124141648973

  11. 设置推送请求,如图

    image-20231124143141499
  12. 等待原作者通过

github上的开源协议

下图出处

94497eee-e34a-4239-8361-b27bc9c299ee-image

img

类似MIT这样的协议,也还是需要版权声明的

版权声明

如果发行的仅仅是可执行软件(不带源码),那就要在软件的某个界面上说明

比如说谷歌浏览器的关于界面中,Google Chrome的诞生离不开Chromium开源项目以及其他开源软件

image-20241125161813840

点开”开源软件”字眼会打开一个页面,列出了一长串的开源软件,其LICENSE和主页(或代码托管地)

image-20241125161936240

详解参考此处

给自己的项目添加开源协议

项目页中点击add file,输入LICENSE就会出现Choose a license template按钮,如下图:

image-20250105170823405

就会出现很多模板供你选择,选好模板后,点击Review and submit后再点击Commit changes...

其实就是在项目下增加了一个LICENSE文件

github action

image-20251210135819075

后面三个步骤,每次总是一样的,因此完全可以自动化完成

简单来说可以

  1. 自己服务器上的应用部署过程自动化
  2. 合作开发的时候自动测试代码

详细来说

根据持续集成的设计,代码从提交到生产,整个过程有以下几步。

(1)提交

流程的第一步,是开发者向代码仓库提交代码。所有后面的步骤都始于本地代码的一次提交(commit)。

(2)测试(第一轮)

代码仓库对commit操作配置了钩子(hook),只要提交代码或者合并进主干,就会跑自动化测试。 测试有好几种。

  • 单元测试:针对函数或模块的测试
  • 集成测试:针对整体产品的某个功能的测试,又称功能测试
  • 端对端测试:从用户界面直达数据库的全链路测试

(3) 构建

通过第一轮测试,代码就可以合并进主干,就算可以交付了。 交付后,就先进行构建(build),再进入第二轮测试。所谓构建,指的是将源码转换为可以运行的实际代码,比如安装依赖,配置各种资源(样式表、JS脚本、图片)等等。 常用的构建工具如下。

  • Jenkins
  • Travis
  • Codeship
  • Strider

(4) 测试(第二轮)

构建完成,就要进行第二轮测试。如果第一轮已经涵盖了所有测试内容,第二轮可以省略,当然,这时构建步骤也要移到第一轮测试前面。

第二轮是全面测试,单元测试和集成测试都会跑,有条件的话,也要做端对端测试。所有测试以自动化为主,少数无法自动化的测试用例,就要人工跑。

需要强调的是,新版本的每一个更新点都必须测试到。如果测试的覆盖率不高,进入后面的部署阶段后,很可能会出现严重的问题。

(5) 部署

通过了第二轮测试,当前代码就是一个可以直接部署的版本(artifact)。将这个版本的所有文件打包( tar filename.tar * )存档,发到生产服务器。

(6) 回滚

一旦当前版本发生问题,就要回滚到上一个版本的构建结果。最简单的做法就是修改一下符号链接,指向上一个版本的目录。

GitHub Actions 是什么?

Github Actions是由Github创建的 CI/CD服务。 它的目的是使所有软件开发工作流程的自动化变得容易。 直接从GitHub构建,测试和部署代码。CI(持续集成)由很多操作组成,比如代码合并、运行测试、登录远程服务器,发布到第三方服务等等。GitHub 把这些操作就称为 actions。

很多操作在不同项目里面是类似的,完全可以共享。GitHub 允许开发者把每个操作写成独立的脚本文件,存放到代码仓库,使得其他开发者可以引用。

如果你需要某个 action,不必自己写复杂的脚本,直接引用他人写好的 action 即可,整个持续集成过程,就变成了一个 actions 的组合。这就是 GitHub Actions 最特别的地方。

GitHub 做了一个GitHub Marketplace ,可以搜索到他人提交的 actions。另外,还有一个Awesome Actions的仓库,也可以找到不少 action。

基础概念

GitHub Actions 有一些自己的术语。

  • workflow (工作流程):持续集成一次运行的过程。
  • job (任务):一个 workflow 由一个或多个 job 构成,含义是一次持续集成的运行,可以完成多个任务。
  • step(步骤):每个 job 由多个 step 构成,一步步完成。

虚拟环境

GitHub Ac­tions 为每个任务 (job) 都提供了一个虚拟机来执行,每台虚拟机都有相同的硬件资源:

  • 2-core CPU, 7 GB RAM 内存, 14 GB SSD 硬盘空间
  • 硬盘总容量为90G左右,可用空间为30G左右

使用限制

  • 每个仓库只能同时支持20个 workflow 并行。
  • 每小时可以调用1000次 GitHub API 。
  • 每个 job 最多可以执行6个小时。
  • 免费版的用户最大支持20个 job 并发执行,macOS 最大只支持5个。
  • 私有仓库每月累计使用时间为2000分钟,超过后$ 0.008/分钟,公共仓库则无限制。
  • 操作系统方面可选择 Win­dows server、Linux、ma­cOS,并预装了大量软件包和工具。

TIPS: 虽然名称叫持续集成,但当所有任务终止和完成时,虚拟环境内的数据会随之清空,并不会持续。即每个新任务都是一个全新的虚拟环境。

workflow 文件

GitHub Ac­tions 的配置文件叫做 work­flow 文件,存放在代码仓库的.github/workflows 目录中。

work­flow 文件采用 YAML 格式,文件名可以任意取,但是后缀名统一为.yml,比如 build.yml。一个库可以有多个 work­flow 文件,GitHub 只要发现.github/workflows 目录里面有.yml 文件,就会按照文件中所指定的触发条件在符合条件时自动运行该文件中的工作流程。

在 Ac­tions 页面可以看到很多种语言的 work­flow 文件的模版,可以用于简单的构建与测试。下面是一个简单的 work­flow 文件示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
name: Hello World  #workflow的名字
on: push #什么时候触发该workflow
jobs: #做什么事的盘点
my_first_job: #第一件事
name: My first job
runs-on: ubuntu-latest #这个事要在什么虚拟机上跑
steps:
- name: checkout
uses: actions/checkout@master
- name: Run a single-line script
run: echo "Hello World!"
my_second_job: #第二件事
name: My second job
runs-on: macos-latest
steps:
- name: Run a multi-line script
env:
MY_VAR: Hello World!
MY_NAME: P3TERX
run: |
echo $MY_VAR
echo My name is $MY_NAME

更多细节参阅

  • on表示什么时候触发,如果写了多个,只要满足其中一条,就会触发Workflow

    • push 提交时触发
    • pull_request 提交pr时触发
    • issues 创建issues时触发
    • schedule 定时触发

    更多触发 workflow 的事件参考官方文档

    1
    2
    3
    4
    5
    6
    7
    8
    on:
    push: #触发workflow执行的时机
    branches:
    - main #限定这有这个分支才触发
    pull_request: #触发workflow执行的时机
    breanches:
    - main #限定这有这个分支才触发
    types: [ opened ] #指定必须是开启一个PR的时候
  • jobs 表示这个workflow要做什么事,可以一一罗列要做的事情,多个job之间并行执行

    • runs-on 表示这个workflow要在什么虚拟机上跑,也可以使用MacOS或Windows等
      • self-hosted 自己的服务器
      • ubuntu-latest 大部分情况下ubuntu-latest都是最好的选择
      • macos-latest:macOS 虚拟机
      • windows-latest:Windows 虚拟机
    • container 如果需要一些基本的环境,可以设置容器,这样就不需要在虚拟机上安装了,比如NodeJS,Python,Go等等
    • steps 编写具体的步骤,每个step可以是一个命令也可以是一个别人封装好的Workflow
      • uses接一个别人封装好的workflow
      • run接一个命令
    • action市场参考此处
    1
    2
    3
    4
    5
    6
    7
    8
    #环境配置流程
    GitHub Actions 服务

    分配运行器 (Runner) ← 必须通过 runs-on 指定

    运行器上启动容器 (Container) ← 可选的 container

    执行工作流步骤

    jobs案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #下面是一个ci持续集成/交付的流程案例
    jobs:
    test:
    runs-on: ubuntu-latest #这个workflow跑最新的ubuntu虚拟机
    container: astral/uv:python3.12-bookworm-slim #需要python3.12的环境
    steps:
    - name: Checkout #name可以不写,name主要是给人看的注释,这里取名Checkout是别人封装好的用于获取代码的workflow
    uses: actions/checkout@v6 #使用的是这个workflow,这里才是指明真正使用什么
    - name: Install Dependency #取名叫安装依赖
    run: uv sync #运行uv sync命令
    - name: Test
    run: uv run pytest tests/
    #可以编写第二个Job

    编写好后只需要将这个workflow push到github仓库上去,在github页面的Actions中就会出现编写好的workflow

下面再附带一个cd持续部署的流程完整案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
name: "Deploy"
on:
push:
tag:
- v* #当push一个以v开头的tag的时候才触发

jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6 #拉取代码
- uses: appleboy/scp-action@v1 #scp命令工作流action
with:
host: ${{ secrets.SERVER_HOST }} #服务器的ip
username: ${{ secrets.SERVER_USERNAME }} #服务器的用户名
key: ${{ secrets.SERVER_SERVER_KEY }} #服务器的密码
source: "./" #从虚拟机中的哪个路径
target: "/home/usr/projects/" #搬到服务器上的哪个路径
- uses: appleboy/ssh-action@v1 #在远程服务器上执行 shell 命令的action
with:
host: ${{ secrets.SERVER_HOST }} #服务器的ip
username: ${{ secrets.SERVER_USERNAME }} #服务器的用户名
key: ${{ secrets.SERVER_SERVER_KEY }} #服务器的密码
script:
cd /home/usr/projects
python main.py

由于敏感信息不能直接填写在该文件中,如github中可以添加到项目-Secrets and variables-Action中的Repository secrets添加各种字段

最小权限原则:使用具有最小必要权限的 SSH 用户 使用密钥认证:建议使用 SSH 密钥而不是密码

与其他部署方式的对比

部署方式 优点 缺点
SSH Action 简单直接,适合现有服务器 需要开放 SSH 端口到互联网
Docker 部署 环境一致,易于扩展 需要 Docker 环境
云平台 CLI 原生集成云服务 平台绑定
Webhook 触发 无需开放 SSH 端口 需要在服务器部署接收服务

与gitlab交互

如何设置ssh key

SSH 秘钥默认储存在账户的主目录下的 ~/.ssh 目录

如:C:\Users\用户\.ssh\

输入命令: ssh-keygen -t rsa -C “your_email@youremail.com”

生成两个文件:

  • id_rsa 私钥
  • id_rsa.pub 公钥

cat id_rsa.pub将这里面的内容复制粘贴到下面图片中的key中

image-20240429150438631

成功后如图:

image-20240429152541770

这样就可以了

之后可以正常使用git与gitlab交互

初次提交到gitlab新项目

一个新项目,不能直接提交代码到master分支(无权限),而应该是新建其它分支,将项目push到新建的分支上,后期再进行merge(发起合并请求)

报错为:![remote rejected]master -> master(pre-receive hook declined) error: failed to push some refs to https:…

git push不上去的原因在于所push的分支权限为protected,只有项目的管理员或者项目的管理员指派的具有相应权限的人才能进行push

具体做法:

1
2
3
4
5
6
git add .
git commmit -m "..."
git branch -m "分支名"#建立新分支并切换过去
#上面这句等同于:git branch 分支名 + git checkout 分支名
git remote add origin 远程仓库地址
git push -u origin 分支名#将新分支上传到远程仓库

图形化git管理软件

lazygit

开源的Git终端界面工具lazygit

SourceTree

image-20211207185614938

新建-创建本地仓库的意思就是 git init

新建-添加已经存在的本地仓库:已经git init的文件夹添加进SourceTree

IDE拓展

[[mac及linux_C++环境#Git相关插件|其他vscode中的Git相关插件参考此处]]

特性 Git Graph GitLens GitKraken
核心功能 图形化分支可视化:类似 gitkSourceTree的可视化界面,清晰展示分支、标签、提交、合并、变基等关系 全方位 Git 信息集成:在代码行内、状态栏、文件树、编辑器标题栏等各处显示丰富的 Git 信息 独立的 Git 图形化客户端:功能完整的独立 Git GUI 应用,也提供 VS Code 扩展进行集成
产品形态 VS Code 扩展 VS Code 扩展 独立桌面应用 + VS Code 扩展
可视化能力 极其强大和直观:专门的可视化视图,支持缩放、过滤、搜索、拖拽操作 内嵌式可视化:提供内嵌在编辑器中的”可视化文件历史”和”提交图”视图 专业级可视化:最直观和美观的图形界面,拖拽操作极其流畅
代码溯源 间接:通过点击提交查看文件变更 核心优势:代码行注释直接显示修改信息 有限:主要通过独立客户端查看,VS Code 扩展中集成度一般
分支管理 直观操作:可视化图上直接拖拽分支进行合并、变基等操作 功能丰富:通过命令面板或状态栏操作分支 最强大:拖拽操作最流畅,分支管理功能最完善
提交操作 支持:暂存、提交、修改等操作 深度集成:强大的提交界面,支持模板、签名验证等 完整支持:提供完整的提交工作流,界面友好
远程仓库集成 基本支持:Fetch/Pull/Push 高级支持:PR/MR 查看、创建等丰富远程操作 深度集成:原生支持 GitHub、GitLab、Azure DevOps 等平台
协作功能 有限的协作功能 强大协作:内置 Glo 看板、团队协作功能
性能表现 大型仓库可能较慢 按需加载,通常较流畅 独立应用,性能优秀
学习曲线 相对较低 相对较高 中等
价格 免费 免费(基础功能)+ 付费(高级功能) 免费(基础功能)+ 付费(专业功能)
主要优势 无与伦比的分支可视化,操作直观 无与伦比的代码溯源,功能全面集成

2021 年发生了一次关键性转变:GitKraken 收购了 GitLens,但承诺GitLens免费部分保持免费开源

GitLens

代码行注释、丰富的文件历史视图、强大的搜索和比较功能是无可替代的生产力工具

关于GitLens在2025年的专业版功能,其核心规则是:大多数高级功能在本地和公开仓库中可免费使用,但在私有仓库中使用则需要GitLens Pro订阅

以下是需要GitLens Pro订阅才能在私有仓库中使用的核心功能清单

功能模块 功能名称 主要用途
核心增强功能 Commit Graph (提交图谱) 提供强大的搜索、过滤功能和可视化操作,用于高级历史浏览和仓库管理。
Visual File History (可视化文件历史) 图形化展示文件的演变历史,包括更改时间、规模和作者。
Worktrees (工作树) 允许同时在不同的分支上工作,而无需干扰主工作区。
协作与工作流 Home View (工作首页) 作为工作流程的指挥中心,集中管理当前工作、任务和最近活动。
Launchpad (任务板) 在VS Code内统一查看和管理来自GitHub、GitLab等平台的Pull Requests和任务。
Cloud Patches (云端补丁) 无需创建分支即可通过链接分享代码改动,用于早期协作和反馈。
AI 辅助 GitKraken AI AI辅助生成提交信息、PR描述、解释提交内容等(需注意有每周令牌限额)。
搜索与比较 高级搜索与比较 在Commit Graph等功能中使用更强大的搜索和过滤条件。
  • Commit Graph的授权情况:正如上文所述,Commit Graph的基础视图功能是免费的,但其增强功能(如强大的搜索和过滤)在私有仓库中需要Pro版本

    免费版可以在本地和公开仓库中体验基本视图

  • 学生优惠:如果你是在校学生,可以通过申请GitHub Student Developer Pack来免费获取GitLens Pro的订阅权益

无限延长GitLens试用时长开源项目

使用方式

参考视频快速入门基本操作

第一次安装好以后建议点击👇🏻位置,可以对需要的编辑器内显示做最开始的配置

image-20251015104744869

新增栏详解

旧版GitLens有自己原生的面板,在新面板中GitLens将自己的面板合到vdcode的原生面板中了,可以在快速配置中设置回独立的面板

  1. COMMITS(提交)

    • 作用:展示当前仓库所有分支的完整提交历史。它相当于一个强化的 git log命令,但以非常直观的图形化界面呈现。

    • 详解:你可以看到每个提交的哈希值、提交信息、作者、日期。点击任意一个提交,可以直接展开查看该提交具体修改了哪些文件(即更改集),并直接对比修改前后的代码差异。这是查看项目演进过程最核心的视图。

  2. REPOSITORIES(仓库)

    • 作用:当你的 VS Code 同时打开了多个包含 Git 仓库的文件夹时,这个视图让你可以轻松在多个仓库之间切换和管理。
    • 详解:如果你的工作区(Workspace)只有一个仓库,这个列表可能就一项。但如果你正在开发一个前端项目和一个后端项目,并同时打开,这里就会列出两个仓库,方便你分别查看它们的状态。
  3. BRANCHES(分支)

    • 作用:集中管理本地和远程的所有分支。括号里的数字 ()表示当前仓库检测到的分支总数(包括本地和远程分支)。
    • 详解:你可以在这里快速查看当前所在分支、其他本地分支以及远程仓库上的所有分支。通常可以直接在这个视图中进行切换分支、创建新分支、删除分支、合并分支等操作,也可以在这个界面做分支的比较,而无需使用命令行。
  4. FILE HISTORY(文件历史)

    • 作用:查看当前激活的单个文件的完整修改历史。
    • 详解:当你点击一个特定的代码文件(如 app.js)后,这个视图会列出所有与该文件相关的提交。这对于追踪某个特定文件是如何随着时间演变而来的非常有用,可以快速定位是哪个提交引入了某个特性或修复了某个 Bug。
  5. LINE HISTORY(行历史)

    • 作用:比“文件历史”更精确,用于查看当前光标所在代码行的修改历史。
    • 详解:当你把光标放在某一行代码上时,这个视图会过滤出只影响过这一行的所有提交。这是神器级别的功能,可以快速回答“这行奇怪的代码是谁、在什么时候、为什么写的?”这样的问题,对于理解代码和排查问题极具价值。
  6. REMOTES(远程仓库)

    • 作用:管理当前本地仓库所关联的远程仓库(如 GitHub, GitLab 等)。括号里的数字 ()表示当前配置的远程仓库数量(通常叫 origin)。
    • 详解:在这里你可以看到远程仓库的地址,并展开查看远程仓库上有哪些分支和标签。方便进行拉取(pull)和推送(push)操作。
  7. CONTRIBUTORS(贡献者)

    • 作用:展示所有对该项目代码库有过提交的贡献者列表。括号里的数字 ()表示贡献者的数量。
    • 详解:点击每个贡献者,可以快速查看他们所有的提交记录、更改的文件统计信息等。这对于了解团队成员的活跃度或者快速过滤某个人的工作内容很有帮助。
  8. STASHES(贮藏)

    • 作用:管理你的“贮藏栈”。贮藏(Stash)是 Git 的一个功能,允许你临时保存当前未完成的工作,以便切换分支去做别的事情。
    • 详解:如果你有暂存过的更改,会在这里列出。你可以方便地查看贮藏的内容、应用(恢复)某个贮藏,或者删除不再需要的贮藏。
  9. TAGS(标签)

    • 作用:管理仓库的标签(Tag)。标签通常用于标记重要的项目节点,如软件发布版本(v1.0.0, v2.1.0)。
    • 详解:在这里你可以查看所有已创建的标签,以及标签对应的提交。方便进行版本管理和发布。
  10. SEARCH & COMPARE(搜索与比较)

    • 作用:这是 GitLens 一个非常强大的功能,允许你进行复杂的搜索和比较操作。
    • 详解
      • 搜索:可以跨整个提交历史、根据提交信息、作者、文件等内容进行搜索。例如,搜索所有包含 “fix bug” 提交信息的提交。
      • 比较:可以比较两个任意的提交、分支、标签或贮藏之间的代码差异。例如,比较 master分支和 develop分支有什么不同,或者比较当前版本和上一个发布版本(Tag)之间的所有更改。
编辑器中图标选项等操作
  • Copy SHA
    功能:复制当前代码行所在提交的完整 Git 提交哈希值(如 d3b0738d…)
    用途:快速获取该行代码的精确版本标识,用于在命令行或其他工具中定位提交
  • Open Changes with Previous Revision
    功能:对比当前提交与上一个提交的差异(即 HEAD~1)
    用途:查看当前提交具体修改了哪些内容(以 diff 形式展示),帮助理解代码变更逻辑
  • Open Blame Prior to this Change
    功能:回溯当前行代码在本次提交前的历史
    用途:显示本次提交之前的作者/提交信息,用于追踪更早的变更记录
  • Reveal in Side Bar
    功能:在 VS Code 的源代码管理侧边栏中定位并高亮显示该提交
    用途:快速在 Git 历史记录面板中找到当前提交,方便查看完整上下文
  • Open in Commit Graph
    功能:在 GitLens 提交关系图中展示该提交
    用途:可视化查看该提交的分支、合并、标签等拓扑关系,分析代码库演进历史
  • Explain Changes
    功能:调用 AI(如 OpenAI)生成对当前提交变更的自然语言解释
    用途:自动解析代码变更意图(如修复 Bug、重构逻辑),降低阅读复杂代码的认知负担
  • Show Team Actions
    功能:显示与团队协作相关的操作(需配置 GitLens 云服务)
    用途:创建 PR/MR 链接、标记提交为”已审查”、添加协作注释(需团队订阅功能)
  • Inspect Commit Details
    功能:完整展示该提交的详细信息面板
    内容包含:作者/日期/提交信息、文件变更统计(+++/—)、父提交哈希、分支/标签关联、代码差异预览
禅模式
  • Zen mode

    禅模式,还你整洁的界面

  • review mode

COMMIT GRAPH
任意两个Commit比较

任意两个Commit比较: COMMITS栏下要比较两个节点的差异,这么做: 第一个节点右键选择Select for Compare,第二个节点右键选择Compare withSelected,然后就可以在SEARCH & COMPARE栏下查看比较结果,如👇🏻图

image-20251015105855692

Behind和Ahead说明任意两个提交节点之间的同步状态

  • Behind(落后)

    当前分支比你要比较的目标分支”落后”了多少个提交

  • Ahead(超前)

    当前分支比你要比较的目标分支”超前”了多少个提交

可以把它想象成一场“代码同步的赛跑”:

  • Behind(落后):你落后了,需要 “追赶(pull)” 别人。
  • Ahead(超前):你领先了,需要 “推送(push)” 你的成果给别人。
interactive rebase交互式变基

COMMITS栏下需要交互式变基的节点右键选择Rebase Current Branch onto Commit

就可以进入交互式变基,任意进行调整非常方便

Git Graph

顶尖的可视化,操作分支非常直观,对于理解复杂的合并历史,解决冲突后的状态特别有帮助

  • Git Graph 的核心优势在于直观的拖拽操作。你可以直接用鼠标拖拽一个分支到另一个分支或提交上,然后选择“合并”、“变基”或“重置”。这种交互方式非常符合人对图形化界面的直觉。
  • GitLens 的提交图更侧重于查看和信息展示。要进行操作(如合并、变基),你通常需要右键点击分支或提交,然后在上下文菜单中找到对应命令。这种方式虽然功能齐全,但在直观性和操作流畅性上不如 Git Graph 的拖拽
场景 推荐工具 理由
日常编码,需要瞬间了解某行代码的修改历史和作者 GitLens 代码行注释功能无可替代,深度集成在编辑流中。
需要理清复杂的分支关系,进行合并、变基等操作 Git Graph 可视化更清晰,拖拽操作极其直观,不易出错。
快速查看当前文件或单行的历史记录 GitLens 它的“快速文件历史”、“时间线”视图非常方便。
宏观浏览整个仓库的提交历史和分支流向 Git Graph 全屏专注视图,布局更适合宏观分析。
处理合并冲突,想看清冲突的来源 两者皆可,但优势不同 GitLens 在代码内集成好,Git Graph 在图形上看分支关系更清楚。

使用方式

如图打开方式,或直接在状态栏点击Git Graph

image-20251015103926014

支持在可视化界面中选中某个节点后,再按住ctrl/command的情况下左键点选另一个节点,比较他们之间的差异

仓库设置位置在👇🏻,用户名,远程URL都可以直接修改,非常方便

image-20251015104328388

Git Extensions

Visual Studio 原生 CodeLens

Professional 和 Enterprise 版本相比,社区版的 CodeLens 仅显示“引用计数”“测试状态” 指示器,不包含类似 GitLens 的 “Git 变更信息”(即最后修改者、提交历史)

Git Extensions 是一个开源、免费、功能极其强大的 Git 图形化客户端。它不是一个简单的 VS 扩展,而是一个独立的应用程序,并提供了与 VS 的集成。它的功能可以看作是 GitKraken 或 Sourcetree 的免费、开源替代品,甚至更强大

以下是其主要功能模块:

a. 强大的图形化界面

  • 提交历史视图:清晰展示分支、标签和合并关系的拓扑图。
  • 文件树视图:直观显示每次提交中变更的文件。
  • 差异对比视图:高亮显示代码的具体变更内容。

b. 便捷的仓库操作

  • 提交更改:方便的暂存、取消暂存、填写提交信息。
  • 分支管理:直观地创建、删除、检出、合并、变基分支(常支持拖拽操作)。
  • 标签管理:创建、删除标签。

c. 高级工具

  • 文件历史/责备注释这是您需要的核心功能。可以查看任何文件每一行的最新修改提交,并追溯完整历史。
  • 解决合并冲突:提供图形化界面来解决合并冲突,比命令行简单得多。
  • 存储管理:方便地创建、应用、删除存储点。
  • 子模块支持:方便地初始化和更新子模块。

d. 与 Visual Studio 的集成

安装 Git Extensions 时,它会为 VS 安装一个插件。集成功能包括:

  • 在 VS 的解决方案资源管理器中显示文件的状态图标(已修改、已添加等)。
  • 在 VS 中直接打开 Git Extensions 的提交对话框。
  • 提供一些右键菜单快捷操作。

e. 其他实用功能

  • 集成文件比较/合并工具:支持配置 Beyond Compare、KDiff3 等专业对比工具。
  • 脚本执行:支持自定义脚本。
  • 翻译:支持多国语言。

Git History

最好用的git比较历史插件

核心功能:

  • 比较任意两个commit

    第一个节点: More - Select this commit

    第二个节点: More - Compare with ….

    然后就可以在边栏的COMMITS中查看变化了

  • 单独查看某个文件历史或行历史

    文件右键点Git:View File History,Git:View Line History

Git Line Author

看到每一行最后一行是谁改的

jetbrains系列IDE中都有右键代码点击annotate with git blame来显示每一行最后修改的作者信息,颜色越暗代表时间越早,颜色越亮代表事间越近
这款插件可以在vscode实现类似的效果,并且额外添加了色相用于标识不同的节点信息(feat(蓝色)/fix(红色)/docs(绿色)/refactor(黄色)/test(紫色)等等)

安装好这个插件之后只需要点击vscode下方小眼睛+横线列表的组合图标即可以开关此功能

Code Kingdom

用于可视化展示项目中每个文代和代码行的开发者贡献情况,帮助团队了解代码的势力分布,即开发人员领地范围的

这款插件如果在cursor,trae等vscode二次开发的IDE中得搜索lirentech才能搜索到

vscode操作记录

Git: Toggle Git Blame Editor Decoration是 切换“代码行末作者装饰”显示 的命令。

它是 VS Code 内置 Git 功能的一部分,能让你在不离开编辑区的情况下,快速、直观地看到每行代码最后的修改者。

拓展git功能的软件

jujutsu

jujutsu开源地址

  • 冲突是一等公民

    冲突不再是会阻塞的下一步工作的刺头,没有任何命令会因发生冲突而失败。冲突信息会被记录在提交中,你可以稍后再解决这些冲突.冲突状态可以进一步进行变基、合并或回退操作。需要注意的是,提交中存储的是冲突的逻辑表示,而非冲突标记

  • 自动变基

    当你修改某个提交时,所有后续提交都会自动基于修改后的版本重新变基。这让“基于补丁” 的工作流变得轻而易举。如果在提交中解决了冲突,该冲突的解决方案也会自动传播到后续提交中。

多人git协作参考

多人git协作参考

将git信息作为版本号

我需要的是

目标 是否自动
✔ 提取分支名 自动
✔ 提取 commit hash 自动
✔ 提取作者 自动
✔ 提取时间 自动
✔ 自动版本递增 自动
✔ 不依赖物理文件 自动集成
✔ 发布时版本规范 自动

基础概念

  • CI(持续集成 / Continuous Integration):把代码提交后,由服务器自动执行“拉代码→编译→跑测试→生成构建产物”的流程。优点是自动、可复现、构建人和时间可以记录。常见工具:Jenkins、Drone、GitHub Actions、Gitea CI(或配合 Drone)。你现在不用立刻学 CI,但了解它很有用(能把构建信息自动注入到程序里)。
  • 构建时注入(build-time injection):在“编译程序”的那一刻,把当前 git 信息写进一个文件(源代码或资源),然后编译进去。这样程序运行时就能读取到“构建时”的准确信息。
  • 运行时读取 .git:程序启动时直接读取仓库数据(需要部署时带上 .git 文件夹,否则不可用)。

常见的几种实现方法(按推荐顺序)

  • 构建文件
  • [运行时直接读取 .git(LibGit2Sharp 等)](#运行时直接读取 .git(LibGit2Sharp 等))
  • [在 CI 中注入环境变量并生成文件(适用于使用 CI 的团队)](#在 CI 中注入环境变量并生成文件(适用于使用 CI 的团队))

构建文件

维度 方案1 手动脚本 方案2 程序集属性 方案3 专用工具
是否本质相同 都是从 Git 取 → 写入程序
难度 ⭐(最简单) ⭐⭐ ⭐⭐⭐⭐
灵活性 ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐⭐⭐
可读性 ⭐⭐⭐⭐ ⭐⭐⭐
自动化 ⭐⭐ ⭐⭐⭐⭐⭐
方案成熟度 原始版 正规版 工业版
  • 在构建时生成一个文件

  • [把信息写到程序集属性(AssemblyInformationalVersion / AssemblyMetadata)](#把信息写到程序集属性(AssemblyInformationalVersion / AssemblyMetadata))

    更标准 / 更“专业”

  • [使用现成工具(Nerdbank.GitVersioning / GitVersion / SourceLink)](#使用现成工具(Nerdbank.GitVersioning / GitVersion / SourceLink))

    几乎”不用管版本了”

在构建时生成一个文件

原理:在构建(或 pre-build)阶段运行 git 命令,把 branch、short hash、作者、提交时间等写成一个 C# 类(比如 VersionInfo.cs)或 version.json,然后编译进程序集。

优点:生成的程序自包含,不依赖目标机器是否安装 git,也可在 CI 环境稳定自动化。适合生产发布

缺点:需要在构建环境能运行 git 或由 CI 注入信息(但大多数开发机/CI 都有 git)。

适用:本地开发/发布/CI 都合适。

把信息写到程序集属性(AssemblyInformationalVersion / AssemblyMetadata)

原理:在构建时把 git 信息写入程序集的元数据(assembly attribute),运行时通过反射读取。

优点:只要程序集在,就能读取;适合嵌入短的信息。

缺点:内容有限,不易保存大量结构化信息(但通常够用)。

适用:想把版本信息放在程序集属性里以便诊断或在 About 窗口显示时读取。

使用现成工具

原理:这些工具自动从 git 生成语义化版本号、并可自动写入版本/元信息到程序集或文件。

优点:功能强大,自动化好,支持 SemVer(语义化版本)等。

缺点:需要学习工具的配置和用法,初学者可能有些复杂。

适用:团队需要完整的版本策略(例如发布 package、遵守 semver)时。

如: Nerdbank.GitVersioning MIT开源 / GitVersion MIT开源/ SourceLink

对比项 Nerdbank.GitVersioning GitVersion MinVer GitInfo CMake内置版本管理 Autover SemVer.Git GitTagVersion
学习成本 ⭐⭐ ⭐⭐⭐⭐ ⭐⭐ ⭐⭐⭐ ⭐⭐ ⭐⭐⭐ ⭐⭐
VS友好度 ✅✅✅ ✅✅✅ ✅✅✅ ⚠️(需CMake集成)
配置简单性 ✅✅✅ ✅✅✅ ✅✅ ⚠️中等 ✅✅ ✅✅
适合小白 ✅✅✅ ✅✅✅ ✅✅ ✅✅ ✅✅
WPF支持 ✅✅✅ ✅✅✅ ✅✅✅ ⚠️(需CMake)
C#支持 ✅✅✅ ✅✅✅ ✅✅✅ ✅✅✅ ⚠️(间接) ✅✅✅ ✅✅✅ ✅✅✅
C++支持 ⚠️(通过MSBuild) ⚠️(通过MSBuild) ⚠️(通过MSBuild) ⚠️(通过MSBuild) ✅✅✅ ⚠️(有限) ⚠️(有限) ⚠️(有限)
许可证 MIT MIT MIT MIT 多种(BSD-like) MIT MIT MIT
Git依赖 必需 必需 必需 必需 可选 必需 必需 必需
跨平台 ✅✅✅ ✅✅✅ ✅✅✅ ✅✅✅ ✅✅✅ ✅✅ ✅✅ ✅✅
云构建支持 ✅✅✅ ✅✅✅ ✅✅✅ ✅✅✅ ✅✅ ✅✅ ✅✅ ✅✅
语义化版本 ✅✅✅ ✅✅✅ ✅✅✅ ✅✅ ✅✅ ✅✅ ✅✅✅ ✅✅
零配置可用 ✅✅✅ ✅✅ ✅✅
Docker支持 ✅✅✅ ✅✅✅ ✅✅✅ ✅✅✅ ✅✅✅ ✅✅ ✅✅ ✅✅
文档完整性 ✅✅✅ ✅✅✅ ✅✅✅ ✅✅ ✅✅✅

综合评分排名

  1. Nerdbank.GitVersioning - 9.2/10
  2. MinVer - 8.8/10
  3. GitVersion - 8.5/10
  4. CMake内置版本管理 - 8.3/10
  5. GitInfo - 7.8/10
Nerdbank.GitVersioning

不足: 默认把“谁编译”的信息注入;分支名不是始终直接可见(可以通过 CI 生成的 BuildInfo 文件补充),因此改用

语义化版本控制在[[架构相关#Semantic Versioning|SemVer]]上进行了一些增强

参考wiki

1
2
3
4
5
6
7
8
9
10
11
#安装nuget包
dotnet add package Nerdbank.GitVersioning

#将软件安装为本地工具
# 创建工具清单(如果没有)
dotnet new tool-manifest # if you don't already have one
#安装nbgv
dotnet tool install nbgv

#安装完成后,通过运行一下命令来验证工具是否正常工作
nbgv --version

基本设置过程包括创建和配置version.json文件,该文件定义您的版本控制方案

nbgv配置

nbgv是 Nerdbank Git Versioning 的命令行工具

  • install: 准备一个项目,以便使用Nerdbank.GitVersioning应用版本标记。
  • get-version: 获取项目的版本信息。
  • set-version: 更新应用于项目的版本标记。
  • tag: 创建一个git标签来标记版本。
  • get-commits: 获取与给定版本匹配的提交。
  • cloud: 与云构建通信以设置构建号和/或其他云构建变量。
  • prepare-release: 通过为当前版本创建发布分支并调整当前分支上的版本来准备发布。
1
2
3
4
5
#生成一个最小的version.json文件
nbgv install

# 获取当前配置
nbgv get-version

所有版本控制设置都由version.json代码仓库中的一个文件控制。该文件定义了基本版本号及其格式和递增方式,该文件可以放在存储库的根目录或任何子目录中,以覆盖该子目录的设置

nbgv get-version打印的结果说明:

字段 含义 示例 用途
Version 完整版本号 1.0.0.29645 文件版本,最后是提交数
AssemblyVersion 程序集版本 1.0.0.0 AssemblyVersion 特性,通常主版本.次版本.0.0
AssemblyInformationalVersion 信息版本 1.0.0-beta+73cddb712c 包含预发布标签和提交哈希
NuGetPackageVersion NuGet 包版本 1.0.0-beta-g73cddb712c NuGet 包使用的版本格式
NpmPackageVersion NPM 包版本 1.0.0-beta.g73cddb712c NPM 包使用的版本格式

默认生成的version.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
//让 VSCode / Rider / VS 识别这个 JSON 的合法字段、自动补全、校验格式,对构建无影响。
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
//NBGV 会根据 Git 提交数、提交 SHA 等补全完整版本
"version": "1.0-beta",
//定义哪些分支算正式分支,正式构建的分支InformationalVersion会变简洁,例如原本1.0-beta.5+gabc123变成1.0-beta
"publicReleaseRefSpec": [
"^refs/heads/v\\d+(?:\\.\\d+)?$"
],
//允许 NBGV 向云构建服务器(如 GitHub Actions/Azure DevOps)写入构建号
"cloudBuild": {
"buildNumber": {
"enabled": true
}
}
}
GitVersion

比较重,适用想要“一个包搞定大多数版本信息并在 CI 中直接使用变量”的场景
GitVersion 的变量默认是 MSBuild 属性和环境变量

信息 GitVersion GitInfo
版本号(语义版)
提交数
条件式规则(GitFlow 等)
分支名
SHA
Dirty
程序运行时直接读取 ❌(需要自己生成g.cs) ✔✔✔(自动)

参考[[CSharp入门#读取编译器属性|需要自己生成g.cs]],实现在程序运行时直接读取

GitInfo
1
dotnet add package GitInfo

在代码中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using static ThisAssembly;

// 显示 Git 信息(未必能保证理解全对)
Console.WriteLine("=== Git 信息 ===");
Console.WriteLine($"当前 Git 哈希值: {ThisAssembly.Git.Commit}");
Console.WriteLine($"当前分支: {ThisAssembly.Git.Branch}");
Console.WriteLine($"是否脏: {ThisAssembly.Git.IsDirty}");
Console.WriteLine($"基标签: {ThisAssembly.Git.BaseTag}");
Console.WriteLine($"提交时间: {ThisAssembly.Git.CommitDate}");
Console.WriteLine($"是否脏字符串: {ThisAssembly.Git.IsDirtyString}");
Console.WriteLine($"基于最后标签的提交数: {ThisAssembly.Git.Commits}");
Console.WriteLine($"完整哈希: {ThisAssembly.Git.Sha}");
Console.WriteLine($"标签: {ThisAssembly.Git.Tag}");
Console.WriteLine($"基于 Git 标签的基础版本号主版本号: {ThisAssembly.Git.BaseVersion.Major}");
Console.WriteLine($"基于 Git 标签的基础版本号次版本号: {ThisAssembly.Git.BaseVersion.Minor}");
Console.WriteLine($"基于 Git 标签的基础版本号修订版本号: {ThisAssembly.Git.BaseVersion.Patch}");
Console.WriteLine($"获取语义化版本中的预发布标签: {ThisAssembly.Git.SemVer.DashLabel}");
Console.WriteLine($"语义化版本来源: {ThisAssembly.Git.SemVer.Source}");
Console.WriteLine($"主版本号: {ThisAssembly.Git.SemVer.Major}");
Console.WriteLine($"次版本号: {ThisAssembly.Git.SemVer.Minor}");
Console.WriteLine($"修订版本号: {ThisAssembly.Git.SemVer.Patch}");
Console.WriteLine($"预发布标签: {ThisAssembly.Git.SemVer.Label}");
//打印如下:
=== Git 信息 ===
当前 Git 哈希值: dfa22e1
当前分支: main
是否脏: True
基标签: 1.2.3
提交时间: 2025-12-09T17:57:53+08:00
是否脏字符串: true
基于最后标签的提交数: 0
完整哈希: dfa22e1bd042a9b2ca051e36d3de76ce840e2770
标签: 1.2.3
基于 Git 标签的基础版本号主版本号: 1
基于 Git 标签的基础版本号次版本号: 2
基于 Git 标签的基础版本号修订版本号: 3
获取语义化版本中的预发布标签:
语义化版本来源: Tag
主版本号: 1
次版本号: 2
修订版本号: 3
预发布标签:

可以利用这些信息设置程序集版本

1
2
3
4
5
6
<!--****.csproj-->
<PropertyGroup>
<AssemblyVersion>$(GitBaseVersionMajor).$(GitBaseVersionMinor).$(GitBaseVersionPatch)</AssemblyVersion>
<FileVersion>$(GitBaseVersionMajor).$(GitBaseVersionMinor).$(GitBaseVersionPatch)</FileVersion>
<InformationalVersion>$(GitSemVerMajor).$(GitSemVerMinor).$(GitSemVerPatch)$(GitSemVerDashLabel)+$(GitCommit)</InformationalVersion>
</PropertyGroup>

在上面信息基础上[CSharp入门#编译人和编译时间获取|参考CSharp中使用MSbuild添加编译人和编译时间显示的案例]]

运行时直接读取 .git(LibGit2Sharp 等)

原理:程序运行时用库读取仓库里的 HEAD、最新 commit 等信息。

优点:信息最“实时”,开发调试方便(不需每次编译都生成文件)。

缺点:部署到用户机器或正式环境时通常不包含 .git,就获取不到信息;还会引入本机依赖。

适用:本地开发或内部测试版本,不推荐用于发布给终端用户的程序。

在 CI 中注入环境变量并生成文件(适用于使用 CI 的团队)

原理:CI 在构建时把 GIT_COMMIT、GIT_BRANCH、构建者名等通过环境变量注入,然后生成 VersionInfo.cs。

优点:生产构建信息更可靠,可记录“谁触发的构建(构建者)”。

缺点:需要配置 CI(需要学习 CI 的使用)。

适用:团队或公司级自动化部署/构建。

git服务器

特性维度 GitLab CE (社区版) Gitea Forgejo (Gitea 分支) Gogs OneDev GitBucket
核心定位 一体化、功能全面的 DevOps 平台 轻量级、快速、功能丰富的协作平台 由社区驱动,注重自由与隐私的协作平台 极致轻量、最简单的协作平台 All-in-One、内置 CI/CD 的 DevOps 平台 类 GitHub、易上手的协作平台
开源协议 MIT (社区版) MIT MIT MIT Apache-2.0 Apache-2.0
开发语言 Ruby on Rails Go Go Go Java (?) Scala
资源占用 高 (推荐 4核 CPU, 4GB+ 内存) 🚀 低 (约 1GB 内存) ✅ 低 (与 Gitea 相近) ✅ 极低 (树莓派可运行) ✅ 中等 较低 ✅
核心功能 仓库管理、Issue、PR、Wiki、CI/CD、容器仓库、监控 仓库管理、Issue、PR、Wiki、Actions、包注册表 仓库管理、Issue、PR、Wiki、Actions、包注册表 仓库管理、Issue、PR、Wiki (专注核心功能) 仓库管理、Issue看板、内置CI/CD、代码搜索 仓库管理、Issue、PR、Wiki、插件系统
CI/CD 方式 强大且内置 (.gitlab-ci.yml) 通过 Actions (兼容 GitHub) 通过 Actions (兼容 GitHub) 需外部集成 (如 Drone, Jenkins) 强大且内置 (图形化或 YAML 配置) 🚀 需外部集成 (如 Jenkins)
特色优势 功能最全面,开箱即用,覆盖 DevOps 全生命周期 社区活跃,功能与资源占用的最佳平衡,迭代快 强调数据主权、社区治理,避免商业公司控制 安装部署最简单,资源占用极低 智能(代码搜索导航)、深度集成CI/CD API 兼容性好,操作类似 GitHub
潜在缺点 资源消耗大,安装和维护相对复杂,高级功能需要企业版 (EE) 功能广度与深度不及 GitLab 生态与市场占有率暂不如 Gitea 功能相对较少,高级协作功能有限 基于 Java,资源占用相对 Go 语言项目稍高 开发活跃度和社区规模相对较小
最适合场景 中大型团队,需要开箱即用的完整 DevOps 解决方案,不介意资源消耗 中小团队及个人项目,追求功能、资源和社区的最佳平衡 注重数据隐私、合规和社区治理的团队 微型团队、个人项目及资源极度受限的环境 需要开箱即用CI/CD的中小团队,喜欢一体化 喜欢 GitHub 风格、寻求简单易用的团队

GitLab

手把手搭建企业级GitLab服务器!零基础保姆教程 | 避坑指南+更新维护+无公网IP线上发布-哔哩哔哩

🛠️ GitLab:一体化 DevOps 平台

GitLab 是一个基于 Git 的一体化 DevOps 平台,覆盖了从项目规划、源代码管理到 CI/CD、监控的整个软件开发生命周期。

  • 它能做什么

    • 代码仓库管理:提供强大的 Git 仓库管理功能,支持代码审查、分支保护、合并请求(Merge Requests)。
    • 内建 CI/CD:通过 .gitlab-ci.yml 配置文件无缝集成持续集成和交付,自动化构建、测试和部署流程。
    • 项目管理:提供问题跟踪(Issue Tracking)、Wiki 文档、看板(Kanban)等功能,帮助团队协同。
    • 多种部署方式:提供 SaaS 托管服务(gitlab.com),也支持私有化部署(社区版免费,企业版付费),适合对数据安全有高要求的企业。
  • 谁适合用

    • 寻求开箱即用、高度集成的 DevOps 解决方案的团队。
    • 希望减少在工具链整合上花费精力的开发者。

gitbucket

GitBucket 是一个由 Scala 驱动的 Git 网络平台,提供以下功能:

  • 简单安装
  • 直观的用户界面
  • 插件高度可扩展
  • 与 GitHub API 兼容

Gitea

特性 Gitea GitBucket
主要语言 Go Scala (JVM)
部署方式 单文件可执行,Docker,群辉套件支持 Java WAR 包 / Docker
资源占用 很低,轻量 较高,需要 JVM
界面风格 类 GitHub / GitLab 简洁 几乎 GitHub 原版风格
功能 Issues、PR、Wiki、CI/CD (Gitea Actions)、Web IDE Issues、PR、Wiki,可通过插件扩展 CI/CD、GitLFS 等
插件/扩展 内置功能多,插件较少 插件机制灵活,可扩展功能
社区活跃度 非常活跃,更新快 不算活跃,更新慢
适合场景 NAS、小团队、个人开发、轻量部署 Java 团队、内部 GitHub 风格、需要插件扩展
长期维护性 高,社区活跃,更新频繁 中,依赖少数维护者,更新较慢
易用性 非常简单,上手快 上手容易,但依赖 JVM 配置

似乎gitea更好

docker部署Gitea

使用docker部署非常简单

  1. 打开 Docker → 注册表

    • 搜索 gitea/gitea
    • 选择 latest 镜像下载
  2. 创建容器

    • 打开 容器 → 添加 → 使用镜像

    • 选择 gitea/gitea:latest

    • 配置基本设置:

      • 容器名称:gitea

      • 启动方式:开机自启

      • 网络:桥接模式(Bridge)

      • 端口映射

        • 容器端口 - NAS端口
        • 3000 - 3000
        • 22 - 2222
      • 卷映射

        nas中的data和git文件夹要自己新建

        • \data - /volume1/docker/gitea/data
        • \git - /volume1/docker/gitea/git
        • \ssl - /volume1/docker/certbot/etc/ 用于结合certbot配置gitea自身的ssl配置,需要将符号链接文件和原本文件的路径都配置上才行

    启动容器后就可以访问 http://NAS_IP:3000 ,会显示Gitea初始化界面

    初始化界面中推荐使用 SQLite(轻量级,直接用文件即可)

使用tail -f /var/log/gitea/gitea.log查看gitea日志,也可以使用docker logs gitea更方便(gitea是容器名)

权限注意事项

gitea中,私有库即使给了协助者可写权限,也不能fetch或clone,但是如果将人员加入到组织中,全部成员设置为管理员,却可以进行正常的全部读写操作

将其他项目迁移到gitea流程

  1. 删除原始凭证(可选)
  2. 迁移流程

删除原始凭证(可选)

界面操作如下

  • Windows系统

    1. 打开“控制面板” -> “用户账户” -> “凭据管理器”。
    2. 在“Windows凭据”或“普通凭据”中,找到与Coding相关的条目并删除
  • macOS系统

    1. 打开“钥匙串访问”应用。
    2. 搜索“Coding”或旧地址,找到并删除对应的“互联网密码”条目
  • Linux:

    使用下面命令行方式

使用命令行删除原仓库凭证如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
### Windows
## 下面是cmd
# 列出所有保存的凭据
cmdkey /list

# 删除Coding相关凭据(将<target>替换为列表中的标识)
cmdkey /delete:<target>

## 下面是powershell
# 查找Coding凭据
Get-ChildItem ~\AppData\Local\GitCredentialManager\credentialstore\

# 删除特定文件(将<file>替换为实际文件名)
Remove-Item ~\AppData\Local\GitCredentialManager\credentialstore\<file>

### MacOS
# 查找Coding相关凭据
security find-internet-password -s "coding.net"

# 删除凭据
security delete-internet-password -s "coding.net"

### Linux
# 编辑凭据文件
vim ~/.git-credentials
# 删除包含coding.net的行
#清除缓存(如果使用cache模式)
git credential-cache exit

### 清除后测试连接
git ls-remote https://coding.net/your/repo.git
#此时应弹出新的认证窗口,证明旧凭据已移除

迁移流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#在原项目目录下移除原本的远程地址
git remote remove origin

#将的新仓库地址添加为新的远程地址(命名为 origin)
git remote add origin <你的Gitea新仓库URL>
# 例如:
# git remote add origin https://gitea.example.com/yourname/yourproject.git

#查看项目的远程仓库信息是否配置上了
git remote -v

#检查是否连接上新的远程仓库了
git fetch origin

#查看远程仓库详情,会显示远程分支
git remote show origin

#执行以下命令(如果没有新凭证会弹出浏览器要求登录),将本地所有分支、所有提交历史、所有标签全部推送到新的Gitea仓库
git push -u origin --all
git push -u origin --tags

#重建原本就已经存在的本地分支与远程仓库的追踪关系
git branch --set-upstream-to="origin/远程分支名" #使当前分支追踪指定的远程分支名

#之后就可以正常使用git与之交互了

gitea配置

gitea/data/gitea/conf/app.ini中配置gitea:

1
2
3
4
5
6
7
8
9
10
11
12
13
[server]
APP_DATA_PATH = /data/gitea
DOMAIN = 192.168.8.6
SSH_DOMAIN = 192.168.8.6
HTTP_PORT = 3000
#ROOT_URL = https://xxx.xxx.xxxx:xxxx #限制只能这个域名访问,不保留这一行就可以随意访问
DISABLE_SSH = true
SSH_PORT = 22
SSH_LISTEN_PORT = 22
LFS_START_SERVER = true
LFS_JWT_SECRET = UewPxQMOLEF7dRIxrCfUKrbm3TxAptRjtUw0cFyRMeE
OFFLINE_MODE = true
REDIRECT_OTHER_PORT = true ; 强制重定向非HTTPS端口

内外网域名统一设置

Gitea链接内外网显示一致

Gitea 的配置文件通常位于安装目录下的 custom/conf/app.ini。使用文本编辑器(如 nanovim)打开它,找到 [server]部分,修改或添加以下两个参数

1
2
3
4
5
6
7
8
9
10
[server]
; 内部监听地址和端口,保持原样即可,这不影响外部访问
HTTP_ADDR = 192.168.8.6
HTTP_PORT = 3000

; !!!最重要的配置:设置Gitea的外部访问根URL !!!
ROOT_URL = https://your-domain.com/
; 请将 your-domain.com 替换为您实际对外访问的域名

; 其他配置...

设置好后检查所有页面上的链接:特别是仓库的“克隆”地址,应该都显示为 https://your-domain.com/username/repo.git,而不是内网 IP

内外网访问一致

目标: 让所有用户(无论内外网)都访问统一的、无端口的标准URL

下面的可以跳过(试错流程),直接看标准流程

通过设置路由器来为内网用户设置DNS解析,将域名在内网解析为ip地址+端口号

1
192.168.8.6:3000    gitea.your-company.com
  • 内网用户:通过路由器 DNS 或 hosts文件将 your-domain.com解析到 192.168.8.6

    1
    192.168.8.6    your-domain.com
  • 外网用户:通过公网 DNS 解析到穿透服务的公网 IP。

  • 这样所有用户都使用 https://your-domain.com访问,无需区分环境。

看起来很美好,但实际上内网DNS解析(或hosts文件)只能解析到IP,无法附带端口号

而外网dns解析却可以解析到IP+端口

因此还需要在内网搭建一个反向代理服务器(如Nginx),让它监听标准的80/443端口,然后将请求转发给Gitea的3000端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
listen 80;
# 如果配置了SSL证书,也监听443
# listen 443 ssl;
server_name your-domain.com; # 你的域名

# 核心配置:将所有请求转发给Gitea
location / {
proxy_pass http://localhost:3000; # 或者 http://192.168.8.6:3000
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

最终效果

访问来源 用户行为 流量路径
外网用户 访问 https://your-domain.com 域名 → 公网IP → (公网反向代理) → 穿透服务 → 内网Nginx(:80) → Gitea(:3000)
内网用户 访问 http://your-domain.com 域名 → 内网IP → 内网Nginx(:80) → Gitea(:3000)

但这样依旧内外网不统一,内网是http,而外网却是https,这对于git来说会视为两个仓库,因此还是有问题,应该参照标准流程来进行

内外网域名统一设置标准流程

生产环境的标准实践,可扩展的企业级架构

架构图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
graph LR
subgraph Clients["客户端"]
LAN_PC["内网客户端"]
WAN_User["外网用户"]
end

subgraph CloudflareNet["Cloudflare 网络"]
CF_DNS["Cloudflare DNS\n(域名解析)"]
CF_Edge["Cloudflare Edge\n(反向代理/CDN/隧道)"]
end

subgraph NAS["群晖 NAS\n(宿主机: 192.168.8.199)"]
subgraph Macvlan["Docker Macvlan 网络\n(192.168.8.0/24 独立IP)"]
Nginx["Nginx 反向代理\n192.168.8.201\n80/443"]
Gitea["Gitea 服务\n192.168.8.202:3000"]
Cloudflared["Cloudflared 隧道\n192.168.8.203"]
Certbot["Certbot\n一次性申请"]
CertbotRenew["Certbot Renew\n自动续期脚本"]
end
end

%% 内网访问
LAN_PC -->|直接访问 http://192.168.8.202:3000| Gitea
LAN_PC -->|访问 nas.jhh1.site → 局域网 DNS 解析到 192.168.8.201| Nginx
Nginx --> Gitea

%% 外网访问
WAN_User -->|https://nas.jhh1.site| CF_Edge
CF_DNS --> CF_Edge
CF_Edge --> Cloudflared
Cloudflared --> Nginx
Nginx --> Gitea

%% Certbot
Certbot -->|使用 dns-cloudflare 插件申请证书| CF_DNS
Certbot -->|挂载证书| Nginx
CertbotRenew -->|定时 renew 证书| Certbot
CertbotRenew -->|证书更新后 reload 服务| Nginx
CertbotRenew -->|可选: docker restart Gitea| Gitea

下面流程图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
flowchart TB
subgraph LAN["局域网 192.168.8.0/24"]
NAS["宿主机 NAS\n192.168.8.199\n(不能直连 macvlan)"]

subgraph Macvlan["Docker macvlan 网络 (nas-macvlan)"]
Nginx["nginx\n192.168.8.201\n80/443\n反向代理 + TLS 证书"]
Gitea["gitea\n192.168.8.202:3000\nGit 服务"]
Cloudflared["cloudflared\n192.168.8.203\n外网隧道"]
Certbot["certbot\n一次性申请证书"]
CertbotRenew["certbot-renew\n自动续期脚本 (定时任务)"]
end
end

%% 内网访问
ClientLAN["内网客户端"] -->|http://192.168.8.202:3000| Gitea
ClientLAN -->|https://nas.jjh1.site → 静态DNS: 192.168.8.201| Nginx
Nginx --> Gitea

%% 外网访问
ClientWAN["外网用户"] -->|https://nas.jjh1.site| CloudflareNet["Cloudflare 节点"]
CloudflareNet --> Cloudflared
Cloudflared --> Nginx
Nginx --> Gitea

%% Certbot
Certbot -->|dns-cloudflare 验证申请证书| CloudflareNet
Certbot -->|证书挂载| Nginx
CertbotRenew -->|周期执行 certbot renew| Certbot
CertbotRenew -->|检测到续期成功后 reload 服务| Nginx
CertbotRenew -->|可选: docker restart gitea| Gitea
image-20250923094526592

为内网环境部署HTTPS证书

使用Let’s Encrypt免费证书(需公网域名)

1
2
3
4
5
# 在内网服务器安装certbot
sudo apt install certbot

# 通过DNS验证申请证书(无需开放80/443端口)
certbot certonly --manual --preferred-challenges dns -d your-domain.com

按提示在域名DNS添加TXT记录验证所有权,证书将生成在 /etc/letsencrypt/live/your-domain.com/

需要配置自动续期
1
2
# 每月自动续期
sudo certbot renew --quiet --post-hook "systemctl reload nginx"

配置内网Nginx HTTPS反向代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# /etc/nginx/sites-available/gitea
server {
listen 443 ssl;
server_name your-domain.com;

# 证书路径(选择方案A或B的路径)
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# 或自签名证书路径
# ssl_certificate /etc/ssl/certs/your-domain.crt;
# ssl_certificate_key /etc/ssl/private/your-domain.key;

# 强化的SSL配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;

location / {
proxy_pass http://localhost:3000; # 保持HTTP转发
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

# HTTP强制跳转HTTPS
server {
listen 80;
server_name your-domain.com;
return 301 https://$host$request_uri;
}
群晖Nginx

群晖的nginx配置方式有所不同,因为其本身有内置nginx占用了常用端口

最好不要动群晖本身的nginx

群晖本身就提供了反向代理服务器的功能,操作位置为: 登录DSM -> 进入 “控制面板” -> “登录门户” -> “高级” 选项卡 -> **“反向代理服务器”**的功能

由于群晖本身内部的实现就基于Nginx实现的反向代理443接口,因此,我们自建nginx来监听443端口会有冲突,可以通过macvlan分配的ip地址中监听443端口,就可以监听443端口,并与群晖本身相独立,相关指令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#新建macvlan网络
docker network create -d macvlan \
--subnet=192.168.8.0/24 \
--gateway=192.168.8.1 \
--ip-range=192.168.8.200/29 \
--aux-address="host=192.168.8.199" \
-o parent=eth1 \
nas-macvlan
#👆🏻这个nas-macvlan网络在NAS重启后会丢失📢📢📢 解决方案,参考下面的 macvlan网络持久化

docker network inspect nas-macvlan#查看新建的nas-macvlan网络

#启动位于macvlan网络中的nginx服务
docker run -d --name nginx-macvlan \
--network nas-macvlan --ip 192.168.8.201 \
-v /volume1/docker/nginx/conf:/etc/nginx/conf.d:ro \
-v /volume1/docker/certbot/etc:/ssl:ro \
nginx:stable

#启动位于macvlan网络中的gitea服务 这个gitea服务必须可以访问到certbot的证书的live和archive目录,这样才能证书可以后自动更新
docker run -d --name gitea-macvlan --network nas-macvlan --ip 192.168.8.202 --restart unless-stopped -p 3000:3000 --memory="4g" -e USER=git -e GITEA_CUSTOM=/data/gitea -v /volume1/docker/gitea/data:/data -v /volume1/docker/gitea/git:/git -v /volume1/docker/certbot/etc:/ssl gitea/gitea:latest

#验证容器状态
# 检查IP地址
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' gitea-macvlan
# 检查重启策略
docker inspect --format '{{.HostConfig.RestartPolicy.Name}}' gitea-macvlan

#启动位于macvlan网络中的内网穿透服务(此处必须要手动指明dns,可能是因为无法使用宿主机的dns服务器的原因,不指明无法正常连接cloudflare服务器进行内网穿透)
docker run -d --name cloudflare-tunnel-macvlan -e TUNNEL_TOKEN=eyJhIjoiYWVlNmYyZDFlMWU2YTFjZTg3NTdl... --dns 192.168.8.1 --dns 223.5.5.5 --network nas-macvlan --ip 192.168.8.203 --restart unless-stopped --memory="4g" cloudflare/cloudflared:latest tunnel --no-autoupdate run

注意一下,👆🏻的容器通过ssh创建的,在群晖nas中的自动重新启动策略是always,即即使正常退出也重启,在界面显示自动重启没有勾选是因为策略不是always,而是unless-stopped,所以实际上不影响出问题自动重启的功能

更改配置的ip流程

1
2
3
4
5
6
7
8
# 1. 停止容器
docker stop gitea-macvlan
# 2. 断开当前网络
docker network disconnect nas-macvlan gitea-macvlan
# 3. 重新连接并指定IP
docker network connect --ip 192.168.8.202 nas-macvlan gitea-macvlan
# 4. 启动容器
docker start gitea-macvlan

检查项目如下

1
2
3
4
5
6
7
8
#检查配置项目是否已加载
sudo nginx -T | grep "gitea.conf"
#检查配置是否通过语法测试
sudo nginx -t
#应用配置更改
sudo systemctl reload nginx
#检查监听状态(不需要,因为与原本配置没有区别,原本配置也会监听tcp和tcp6)
sudo netstat -tuln | grep :443

macvlan网络持久化

设置开机后执行创建nas-macvlan的指令:

控制面板 - 任务计划 - 新增 - 触发的任务 - 用户定义的脚本 - 任务设置 - 用户定义的脚本 中填入:

1
2
3
4
/bin/sh -c 'count=0; until docker info >/dev/null 2>&1; do if [ $count -ge 30 ]; then echo "Docker未在预期时间内启动"; exit 1; fi; count=$((count+1)); sleep 1; done; if ! docker network inspect nas-macvlan >/dev/null 2>&1; then docker network create -d macvlan --subnet=192.168.8.0/24 --gateway=192.168.8.1 --ip-range=192.168.8.200/29 --aux-address="host=192.168.8.199" -o parent=eth1 nas-macvlan; fi'

#待反馈信息的版本
/bin/sh -c 'count=0; until docker info >/dev/null 2>&1; do if [ $count -ge 30 ]; then echo "错误:Docker未在30秒内启动"; exit 1; fi; count=$((count+1)); sleep 1; done; if docker network inspect nas-macvlan >/dev/null 2>&1; then echo "网络nas-macvlan已存在"; else echo "正在创建macvlan网络..."; docker network create -d macvlan --subnet=192.168.8.0/24 --gateway=192.168.8.1 --ip-range=192.168.8.200/29 --aux-address="host=192.168.8.199" -o parent=eth1 nas-macvlan && echo "网络创建成功" || echo "网络创建失败"; fi'

p.s. 在大多数系统的计划任务或脚本输入框中,我们需要将命令写为一行,用空格分隔各个参数

内网DNS解析

  • 在路由器或内网DNS服务器将 your-domain.com解析到内网反向代理服务器IP(如 192.168.8.201
  • 或每台设备修改hosts:
1
192.168.8.201 your-domain.com

certbot配置

[[运维#certbot|参考运维笔记中的Certbot部分]]

gitea分支保护配置

Gitea 的分支保护设置对于企业规范而言,核心在于平衡代码质量、安全管控与团队协作效率

保护规则 功能描述 适用场景 企业规范建议
禁止强制推送 防止覆盖历史提交,确保提交历史可追溯 所有重要分支 对所有保护分支开启
要求Pull Request 强制代码审查流程,合并前必须通过PR 所有重要分支 对所有保护分支开启,根据分支重要性设置不同批准人数
要求状态检查 确保CI测试通过,代码质量达标 集成测试分支 配置必要的状态检查,如构建、测试、lint检查
要求签名提交 验证提交者身份,增强安全性和责任追溯 安全敏感项目 按需开启,通常用于核心库或高风险项目
限制推送权限 指定特定用户或团队拥有推送权限 核心代码分支 通常仅授权技术负责人或核心维护者

企业中的代码库通常需要保护以下关键分支:

  1. 生产分支(如 mainmaster: 这是最严格保护的分支,通常对应生产环境。
  2. 开发分支(如 develop: 集成分支,用于合并各个功能分支,稳定性次于主分支但仍需保护。
  3. 发布分支(如 release/*: 用于测试和准备发布的分支。
  4. 热修复分支(如 hotfix/*: 用于紧急修复生产环境问题。

生产分支

这是你的“黄金标准”,保护应该最为严格。

  • 要求 Pull Request: 必须开启。所有合并必须通过代码审查。
  • 所需批准数: 建议至少 2 人。重要修改应获得多位核心成员的认可
  • 禁止强制推送: 必须开启。防止历史提交被覆盖。
  • 要求状态检查: 必须开启。必须通过所有CI流程(如编译、单元测试、集成测试、安全扫描等)才能合并。
  • 要求签名提交: 按需开启。对于安全要求极高的项目,可以要求所有提交必须经过GPG签名验证。
  • 限制推送权限: 强烈建议。通常只授权给团队负责人或少数核心成员,其他开发者通过PR协作。

开发分支

开发分支是活动最频繁的集成分支,需在质量和效率间权衡。

  • 要求 Pull Request: 必须开启。功能分支合并到develop前必须经过审查。
  • 所需批准数: 至少 1 人。通常由项目核心开发人员或团队负责人批准即可
  • 禁止强制推送: 必须开启
  • 要求状态检查: 必须开启。至少需要通过基础CI构建和测试。
  • 限制推送权限: 通常不开启,允许所有开发者创建指向develop的PR。但可根据团队成熟度调整。

分支命名规范

清晰的分支命名能极大提升管理效率。推荐以下规范:

  • 功能分支: feat/简短功能描述,例如 feat/user-authentication
  • 修复分支: fix/问题简要描述,例如 fix/login-validation
  • 文档分支: docs/更新内容描述,例如 docs/api-reference
  • 重构分支: refactor/修改模块描述,例如 refactor/database-layer
  • 发布分支: release/版本号,例如 release/v1.2.0
  • 热修复分支: hotfix/紧急问题描述,例如 hotfix/critical-security-patch
1
2
3
4
5
6
7
8
9
10
11
feat新功能
fix:错误修复
docs文档更新
style代码格式化
refactor代码重构
perf性能提升
test:测试相关
build构建系统
ciCI 配置
chore维护工作
revert撤销更改

实际保护配置

Git相关好玩的工具

命令行工具,根据 Git 提交记录生成周报weekly-git-summary

一键将 GitHub 仓库的所有 issue 导出,保存为 Markdown 文件issue2file]

AI自动代码审查

AI-Codereview-Gitlab

已经支持gitea了!