文章

Git入门基础知识汇总

包含入门Git所需要的大部分基础知识,包括本地仓库与远程仓库的使用与管理、多人协作的基本方式、忽略列表和属性配置文件的使用、开源协议等

Git入门基础知识汇总

可以互补地参考这个网站上的教程

一、基本配置

1.1 关于版本控制

  • git:分布式版本控制工具,每个电脑均有完整的版本库,可以有共享版本库或是直接相互推送

工作流示意.png

  • svn/cvs:集中式版本控制工具,版本库存于中央服务器,必须联网工作

1.2 Git的下载安装

1.2.1 Windows端

  • 此处下载安装到合适的路径即可,记得检查环境变量的配置,配置过程忽略
  • 笔记记录的Git相关操作示例除非特别说明是Linux,都是在Windows端进行的,其实大部分指令都是两端通用的,除了小部分指令如git rev-parse HEAD^1在Windows端会有点问题

1.2.2 Linux端

  • 运行下列指令进行安装
1
sudo apt-get install git

1.3 配置基本信息

  • 下载好了Git后,打开git bash命令行,进行如下的操作设置用户信息
1
2
3
//注意引号前的空格
git config --global user.name "your_name"
git config --global user.email "email_address"
  • 理论上名字和邮箱可随意填写,但是请注意如果你填的邮箱和你使用SSH链接到的Github账户不一致的话,你从本地push到远程仓库的提交记录处显示的提交者就会无法与你的帐号产生链接,如下图的红框所示,这样的灰色头像是无法点击并跳转到你的Github主页的,同时如果你在本地这样提交给你的一个fork过来的仓库并向上游仓库提交了一个PR的话,即使PR被接受了,Contributor列表也不会出现你的账户;至于此图下面的蓝色框为什么会有头像并且可以跳转呢?是因为这是我在Github网页端进行的提交(貌似这样的提交一般会在右边有个Verified 标识)

邮箱不匹配的后果.png

  • 如果你想避免上面的这种灰色头像,那么请你设置本地Git的邮箱和你的Github的Primary Email邮箱地址一致,必须一模一样,同一邮箱的不同别名也不行(你猜我为什么会知道上面这些东西)
  • 使用以下指令可以查看配置的信息
1
2
git config --global user.name
git config --global user.email
  • 关于SSH密钥的配置,参见远程仓库的管理相关章节笔记

1.4 配置常用指令别名

  • 用户目录创建.bashrc文件
  • 在该文件中输入配置内容即可
1
2
3
4
alias git-log='git log --pretty=oneline --all --graph --abbrev-commit'
# 输出git提交日志
alias ll='ls -al'
# 用于输出当前目录所有文件及其基本信息
  • 在git bash中执行source ~/.bashrc

1.5 其他操作

  • 复制粘贴:bash窗口内,选中即复制,中键即粘贴,别用ctrl+cv
  • 清屏:直接clear即可

二、本地仓库管理

2.1 获取本地仓库

  • 选定某一目录为该项目的本地仓库,在此进入git bash,执行命令git init初始化当前目录为一个git仓库
  • 如若创建成功,则能够看到该目录下多了一个隐藏的.git文件
1
2
3
4
5
$ ls -al
total 20
drwxr-xr-x 1 zjc18 197609 0 Mar  4 17:04 ./
drwxr-xr-x 1 zjc18 197609 0 Mar  4 17:03 ../
drwxr-xr-x 1 zjc18 197609 0 Mar  4 17:04 .git/

2.2 修改的上传与查看

commit提交最好要写上简略的修改内容,以及日期

区的示意.png

  • git工作目录下对于文件的修改,如增删改等,存在三个状态,一个是未跟踪,即仅仅在工作区做了修改,一个是已暂存,即把修改上传到了暂存区,一个是成为了一次提交记录,即从暂存区提交到了仓库
  • 指令touch file_name用于创建文件,git status用于查看当前仓库状态
1
2
3
4
5
6
7
8
9
10
11
$ touch test1.txt
//指令创建文件

$ git status
//查看状态
On branch master
No commits yet
Untracked files:
  (use "git add <file>..." to include in what will be committed)
		test1.txt
nothing added to commit but untracked files present (use "git add" to track)
  • 使用git add上传修改到暂存区
1
2
3
4
5
6
7
8
9
10
11
git add test1.txt
//上传特定修改
git add .
//上传所有修改

$ git status
On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
		new file:   test1.txt
  • 使用git commit将暂存区的修改提交至仓库,并作注释,形成一个版本
1
2
3
4
5
6
7
8
9
$ git commit -m "commit a file test1.txt"
//此处-m后是注释内容,注意引号前的空格
[master (root-commit) 5f69526] commit a file test1.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test1.txt

$ git status
On branch master
nothing to commit, working tree clean
  • 可以使用git log查看提交日志
1
2
3
4
5
$ git log
commit 5f6952682038e3aae37980c3f00b89763af2ec5d (HEAD -> master)
Author: WhythZ <jichenz@qq.com>
Date:   Mon Mar 4 17:36:25 2024 +0800
	commit a file test1.txt
  • 对当前文件夹进行修改,并再次提交
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
$ git status
//对test1.txt文件写入了一行文字后,再查看状态
On branch master
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:   test1.txt
no changes added to commit (use "git add" and/or "git commit -a")

$ git add .
//提交到暂存区

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
		modified:   test1.txt

$ git commit -m "update file1.txt"
//提交到仓库
[master 06afd53] update file1.txt
 1 file changed, 1 insertion(+)

$ git status
On branch master
nothing to commit, working tree clean

$ git log
//可以看到这里已经有了两次提交了,上面为最新提交
commit 06afd539bf30bd1f33262895d2d4609ad8071d0f (HEAD -> master)
Author: WhythZ <jichenz@qq.com>
Date:   Mon Mar 4 17:55:00 2024 +0800
	update file1.txt

commit 5f6952682038e3aae37980c3f00b89763af2ec5d
Author: WhythZ <jichenz@qq.com>
Date:   Mon Mar 4 17:36:25 2024 +0800
	commit a file test1.txt
  • 可以用git log --pretty=oneline --all --graph --abbrev-commit查看简易日志,其中--pretty=oneline将提交信息显示为一行,--all显示所有分支,--abbrev-commit使得输出的commitID更为简短,--graph以图像形式显示
1
2
3
$ git log --pretty=oneline --all --graph --abbrev-commit
* 06afd53 (HEAD -> master) update file1.txt
* 5f69526 commit a file test1.txt

2.3 版本回退

2.3.1 reset --soft

  • 使用git reset --soft <commit_id>会将当前分支的HEAD移动到指定的<commit_id>版本上,该版本之后的所有提交仅会从提交历史中消失,若是--hard指令,那么所有文件的增删改都会被删除,而--soft不会
  • 所有被撤销的提交的更改仍然在你的工作目录中,工作目录中的文件不会被更改,所有的文件修改、新增或删除状态都会保持原样,这些更改会保留在你的暂存区中

2.3.2 reset --mixed

  • 如果git reset不指定后缀,则默认为git reset --mixed <commit_id>
  • 你指定的版本之后的所有commit都将从历史记录里消除,但是文件的增删改都会被保留在文件系统里,处于未被git add .提交到暂存区的状态

2.3.3 reset --hard

  • 使用git reset --hard <commit_id>进行版本的回退,所有被撤销的提交的更改将不会出现在你的工作目录或暂存区中,即从指定的<commit_id>之后的文件修改、新增或删除都会丢失
1
2
3
4
5
6
$ git reset --hard 5f69526
HEAD is now at 5f69526 commit a file test1.txt

$ git-log
//使用别名
* 5f69526 (HEAD -> master) commit a file test1.txt
  • 可以看到版本号06afd53消失,但仍可通过此版本号回溯,若清屏丢失了版本号,可以通过git reflog看到已经删除的提交记录
1
2
3
4
$ git reflog
5f69526 (HEAD -> master) HEAD@{0}: reset: moving to 5f69526
06afd53 HEAD@{1}: commit: update file1.txt
5f69526 (HEAD -> master) HEAD@{2}: commit (initial): commit a file test1.txt

2.4 分支管理

分支意味着可以把工作从主线上分离开来进行诸如重大bug修改、新功能开发等,以免影响主线

2.4.1 分支的基本操作

  • 查看所有本地分支,直接git-log也能看到相关信息
1
git branch
  • 查看所有远程分支
1
git branch -a
  • 创建分支
1
git branch branch_name
  • 切换分支
1
git checkout branch_name
  • 创建并切换到新分支
1
git checkout -b branch_name
  • 删除分支,不能删除当前分支
1
2
3
4
git branch -d branch_name
//删除分支时需要做各种检查
git branch -D branch_name
//不做任何检查,直接删除分支

2.4.2 开发中的分支规范

  • 开发中有固定的不会删除的两条分支,master和develop,前者是开发对象的稳定版本,后者是开发功能的分支;hotfix分支是为master分支修复bug所用,最终分别合并到master和develop上;feature分支开发测试完成后最终合并到develop分支上

开发中的分支规范示意.png

2.4.3 分支的merge合并

2.4.3.1 merge的基本使用方法
  • 使用git merge branch_name会将branch_name分支合并到当前工作分支下,合并时会弹出一个文本编辑器的窗口,关闭窗口,等待合并完成即可
  • 以下从头开始创建一个仓库,观察各项操作及其反馈: 1、创建了一个仓库,并在master中添加了t1文件并提交至仓库 2、创建分支b1并切换至分支b1,在此添加t2文件并提交,此时此处有t1和t2文件 3、此时再返回master分支,可以看到此处没有t2文件,只有t1文件,此时再添加t3文件,然后合并文件,会发现master上t1,t2,t3均存在了
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
zjc18@WhythZROG MINGW64 ~/Desktop/gittest
$ git init
Initialized empty Git repository in C:/Users/zjc18/Desktop/gittest/.git/

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ touch t1

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git add .

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git commit -m "add t1 in master"
[master (root-commit) 25cf764] add t1 in master
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 t1

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git checkout -b b1
Switched to a new branch 'b1'

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (b1)
$ touch t2

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (b1)
$ git add .

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (b1)
$ git commit -m "add t2 in b1"
[b1 03fb221] add t2 in b1
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 t2

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (b1)
$ git checkout master
Switched to branch 'master'

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ touch t3

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git add .

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git commit -m "add t3 in master"
[master 2c183db] add t3 in master
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 t3

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git-log
* 2c183db (HEAD -> master) add t3 in master
| * 03fb221 (b1) add t2 in b1
|/
* 25cf764 add t1 in master

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git merge b1
Merge made by the 'ort' strategy.
 t2 | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 t2

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ :
.git/ t1    t2    t3

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ :
.git/ t1    t2    t3

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git-log
*   27aba7b (HEAD -> master) Merge branch 'b1' :wq
|\
| * 03fb221 (b1) add t2 in b1
* | 2c183db add t3 in master
|/
* 25cf764 add t1 in master

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ ll
total 20
drwxr-xr-x 1 zjc18 197609 0 Mar  4 21:05 ./
drwxr-xr-x 1 zjc18 197609 0 Mar  4 21:02 ../
drwxr-xr-x 1 zjc18 197609 0 Mar  4 21:09 .git/
-rw-r--r-- 1 zjc18 197609 0 Mar  4 21:03 t1
-rw-r--r-- 1 zjc18 197609 0 Mar  4 21:05 t2
-rw-r--r-- 1 zjc18 197609 0 Mar  4 21:04 t3
2.4.3.2 merge合并的快进模式
  • 当从master分出一个分支branch并在其上做了修改后,同时master上没有做修改,此时branch上的修改比master上更超前,若要把branch合并到master上,则会进行快进模式
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
zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git-log
* 3ce6a50 (HEAD -> master) add test_1.txt to master
* 5d93952 add test.txt to master

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git checkout -b branch
Switched to a new branch 'branch'

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (branch)
$ touch test_2.txt

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (branch)
$ git add .

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (branch)
$ git commit -m "add test_2.txt to branch"
[branch d078daa] add test_2.txt to branch
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test_2.txt

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (branch)
$ git checkout master
Switched to branch 'master'

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git-log
* d078daa (branch) add test_2.txt to branch
* 3ce6a50 (HEAD -> master) add test_1.txt to master
* 5d93952 add test.txt to master
  • 此时可以看到HEAD指向的master在分支branch修改的d078daa下方,此时我们合并branch到master上
1
2
3
4
5
6
7
8
9
10
11
12
13
zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git merge branch
Updating 3ce6a50..d078daa
Fast-forward
 test_2.txt | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test_2.txt

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git-log
* d078daa (HEAD -> master, branch) add test_2.txt to branch
* 3ce6a50 add test_1.txt to master
* 5d93952 add test.txt to master
  • 可以看到HEAD指向的master被提到了上面,而branch将作为d078daa的分支,我们分别在两个分支上进行一些提交就可以看出这一点,如下所示
1
2
3
4
5
6
7
8
zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git-log
* 46018e9 (branch) add test_4 to branch
| * 58f0924 (HEAD -> master) add test_3.txt to master
|/
* d078daa add test_2.txt to branch
* 3ce6a50 add test_1.txt to master
* 5d93952 add test.txt to master
2.4.3.3 merge合并的冲突解决
  • 当在分支A修改的部位与在分支B修改的部位一样且修改的结果不一样时,若进行合并,则会出现冲突而无法自动合并,需要手动解决冲突。如下列例子,master和b1分支分别在空文件test.txt第一行上写入了不同的内容,尝试合并出现冲突
1
2
3
4
5
6
7
8
9
10
11
12
zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git-log
* 3eb638e (b1) write count=2
| * 32c35d1 (HEAD -> master) write count=1
|/
* 9ca859b add test.txt

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git merge b1
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.
  • 此时test.txt文件内的内容如下,上头是当前分支的内容,下头是被合并过来的分支上的内容
1
2
3
4
5
<<<<<<< HEAD
count=1
=======
count=2
>>>>>>> b1
  • 此时我们直接将多余内容删除,改成我们所需要的样子,比如
1
count=1
  • 或者是
1
count=2
  • 甚至可以是别的任意内容
1
count=114514
  • 修改完成后,在bash直接git add .然后git commit,注意这里可以不用-m添加备注,因为系统知道你在merge,会自动生成相关备注
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master|MERGING)
$ git add .

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master|MERGING)
$ git commit
[master 90f461e] Merge branch 'b1'

zjc18@WhythZROG MINGW64 ~/Desktop/gittest (master)
$ git-log
*   90f461e (HEAD -> master) Merge branch 'b1'
* 
|\
| * 3eb638e (b1) write count=2
* | 32c35d1 write count=1
|/
* 9ca859b add test.txt

2.4.4 分支的rebase合并

三、远程仓库管理

3.1 常用托管服务

  • github,gitee,gitlib
  • 前两者是第三方托管,保密项目的安全性保障不好说,而后者可以自己搭建

3.2 SSH配置

3.2.1 Windows端

  • 若用HTTPS方式同步,则每次同步Github远程仓库都需要填写用户名与密码,而且一般公司只会提供SSH(Secure Shell)方式,因为通过SSH可以把所有传输的数据进行加密,更安全
  • 使用SSH方式需要先生成密钥,并在Github进行配置公钥
  • 在Git命令行中执行此命令生成公钥和私钥,一路回车或者y即可
1
ssh-keygen -t rsa
  • 生成后可以在C:\Users\user_name\.ssh\目录下找到公钥id_rsa.pub和私钥id_rsa,复制id_rsa.pub文件的内容
  • 然后到Github登录自己需要进行工作的帐号,进入Settings页面的SSH and GPG Keys处,点击New SSH Key绿色按钮,将刚才保存的id_rsa.pub文件内容复制到Key字段,至于Title自己起个名字,最好是能代表当前所配置的机器的名字,然后点Add SSH key,会要求输入当前Github账号的密码,按要求输入即可
  • 配置完成后可以使用如下指令检测是否配置成功
1
ssh -T git@github.com

3.2.2 Linux端

参考官方文档

  • 使用同样指令(有很多种指令,上面用到的这种基本就可以了)创建公钥文件,命令执行完会有三步询问,直接全部按回车即可,生成的公钥文件会保存在本机的~/.ssh目录下
  • 创建完成后进~/.ssh目录,将公钥id_rsa.pub的内容复制出来
  • 后续步骤和上面Windows端的一样

3.2.3 两种Key Type的区别

  • GitHub中的SSH key分为两种类型
    • Authentication Keys(身份验证密钥):
      • 用于身份验证,允许您与GitHub进行安全通信,例如通过SSH连接
      • 当您从本地系统与GitHub进行交互时,这是用于验证您身份的密钥
      • 通常,这与访问和推送代码相关
    • Signing Keys(签名密钥):
      • 用于对Git提交进行数字签名,以验证提交的真实性和完整性
      • 这是在Git操作中确保提交的来源和内容未被篡改的一种方式
      • 通过签署提交,您可以确保提交是由特定的私钥持有者创建的

3.3 远程仓库基本操作

3.3.1 链接远程仓库

  • 若是从Github克隆下来的仓库,自动会链接本地仓库与远程仓库,若是本地git init创建的,则需要通过下面的发布指令先进行添加,origin可替换为为你的远程仓库起的一个名字
  • 通过如下方式添加远程仓库,即把本地仓库发布到远程仓库(注意链接是SSH的而不是HTTPS)
1
git remote add origin repo_url
  • 查看已配置的远程仓库,得到的返回形式是仓库服务器的名称(如origin) repo_url
1
git remote -v

addSSHrepo.png

  • 如果远程仓库的名称改变了,则其链接也会发生改变,此时需要使用如下指令更新本地对应仓库的远程仓库地址,然后再通过上面的指令检查是否更改成功
1
git remote set-url origin new_url_of_repo

3.3.2 推送本地仓库

  • 使用push推送本地内容到远程仓库originmaster分支,第一次使用建议加-u,这会将本地的master分支和远程的master分支关联起来,在以后的push或者pull时就可以简化命令
1
2
git push -u origin master
git push origin master
  • 在特殊情况下还可以使用强制推送用于撤回提交或者覆盖提交历史
    • 用于撤回提交:若我们错误地push了一个版本到远程仓库中,此时我们应该使用git reset等将本地仓库回滚到上一个版本,然后用本地的项目覆盖掉远程仓库的提交历史,此时Git会阻止你这样做,因为这些更改在远程分支上仍然存在,使用 --force 可以强制将这些删除的提交从远程仓库中移除
    • 覆盖提交历史:正常情况下,Git不会允许你推送一个与远程分支历史不兼容的本地分支(甚至可以是用完全不同的项目来覆盖掉现有远程仓库),因为这可能会导致数据丢失,使用--force可以忽略这种保护机制,直接覆盖远程仓库中的历史记录
1
git push origin master --force

3.3.3 移除无效远程仓库

  • 当你打错了远程仓库的地址想要重新进行链接,或想修改远程仓库的别名,指令rm short_name可以从本地仓库移除远程仓库short_name(注意此处应该使用远程仓库别名如origin,而非远程仓库的链接),该操作不会对远程仓库本身产生任何影响
1
git remote rm origin

3.4 多人协作与贡献

四、忽略列表

4.1 作用

  • 在项目根目录下,即与.git文件夹同一目录下,.gitignore文件内写入需要忽略的东西,即可使git对这些东西不进行版本控制,从而不需要关注这些对于版本管理无意义的文件,保持版本仓库的整洁性,也可节省存储空间
  • 文件的匹配是基于字符串的,而非正则表达式

4.2 语法

4.2.1 注释

  • 使用#号开头的行被视为注释
1
#注释内容

4.2.2 忽略xxx文件

  • 对于要忽略的东西,每一行描述一个需要忽略的模式,可以是文件、文件夹或者通配符
1
2
3
4
5
test
#表示项目所有文件名中含有"test"的文件都将被忽略,比如名为"test"的文件夹会被忽略,名为image_test.png"的文件也会被忽略

test.py
#只会忽略名为"test.py"的文件
  • 星号*.后表示忽略以.前字符串为名的不论是什么后缀的所有文件;其在前同理类推
1
2
3
4
5
test.*
#忽略所有名为test的带后缀名的文件,比如"test.py"、"test.jpg"等

*.log
#表示忽略掉所有以".log"为后缀的文件
  • 配合路径来使用
1
2
path/test.*
#忽略在路径"path"文件夹内的所有名为test的带后缀文件
  • 通过!开头表示取消忽略,可以覆盖掉之前有过的忽略规则
1
2
3
4
test.py
...
!test.py
#文件"test.py"最终不会被忽略
  • 使用/开头,表示从项目的根目录开始匹配,否则会在项目的所有目录中搜索
1
2
/test
#表示根目录下名为test的文件夹及其内容将被忽略
  • 使用/结尾,表示匹配的是文件夹,而非同名文件
1
2
/build/
#忽略掉根目录下的build文件夹(及其所含内容)

4.2.3 除了xxx之外全部忽略

  • 即我只想保留xxx文件夹,那么可以把下面的几行写入忽略列表文件
1
2
3
4
5
6
7
8
9
10
# 忽略所有文件
/*

# 不忽略xxx文件夹
!xxx/

# 不忽略xxx文件夹内的所有内容
!xxx/**

# 其它要保留的的根目录下的单独文件也要列出来哦

4.2.4 案例

  • 官方提供了很多模板,如下面是Unity项目的忽略列表模板
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# This .gitignore file should be placed at the root of your Unity project directory
#
# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore
#
/[Ll]ibrary/
/[Tt]emp/
/[Oo]bj/
/[Bb]uild/
/[Bb]uilds/
/[Ll]ogs/
/[Uu]ser[Ss]ettings/

# MemoryCaptures can get excessive in size.
# They also could contain extremely sensitive data
/[Mm]emoryCaptures/

# Recordings can get excessive in size
/[Rr]ecordings/

# Uncomment this line if you wish to ignore the asset store tools plugin
# /[Aa]ssets/AssetStoreTools*

# Autogenerated Jetbrains Rider plugin
/[Aa]ssets/Plugins/Editor/JetBrains*

# Visual Studio cache directory
.vs/

# Gradle cache directory
.gradle/

# Autogenerated VS/MD/Consulo solution and project files
ExportedObj/
.consulo/
*.csproj
*.unityproj
*.sln
*.suo
*.tmp
*.user
*.userprefs
*.pidb
*.booproj
*.svd
*.pdb
*.mdb
*.opendb
*.VC.db

# Unity3D generated meta files
*.pidb.meta
*.pdb.meta
*.mdb.meta

# Unity3D generated file on crash reports
sysinfo.txt

# Builds
*.apk
*.aab
*.unitypackage
*.app

# Crashlytics generated file
crashlytics-build.properties

# Packed Addressables
/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin*

# Temporary auto-generated Android Assets
/[Aa]ssets/[Ss]treamingAssets/aa.meta
/[Aa]ssets/[Ss]treamingAssets/aa/*

4.3 多个忽略规则间的关系

  • 在项目目录里,可以通过多个.gitignore文件来实现在不同的子目录中使用不同的规则来进行版本控制,比如下面的这一个文件层级关系
1
2
3
4
5
6
7
8
9
10
11
- project/
	- .gitignore (规则A)
	- subdir_1/
		- .gitignore (规则B)
		- file_1
	- subdir_2/
		- .gitignore (规则C)
		- file_2
	- subdir_3
		- .gitignore (规则D)
		- file_3
  • 这个项目根目录内含有一个通用的忽略规则A,而三个子文件夹内各自写有自己的忽略规则B、C、D,此时子文件夹中的规则是会有可能覆盖掉(类似多态virtual被override覆盖掉的那种感觉)规则A的某些部分的,比如上面提到过的使用!取消掉对某文件的忽略便可在此应用

五、属性配置

5.1 为仓库添加.gitattributes文件

官方提供了语法文档以及针对各种开发环境的文件模板,我们在项目的根目录下创建一个.gitattributes文件并添加相应规则即可

5.1.1 配置.gitattributes文件的意义

  • .gitattributes是一个文本文件,文件中的一行定义一个路径的若干个属性,主要用于定义每种文件的属性,以方便Git帮我们统一管理
1
要匹配的文件模式 属性1 属性2 ...

5.1.2 .gitattributes文件中可以定义的属性

5.1.2.1 text
  • 用于控制行尾的规范性,如果一个文本文件是规范的,则Git库汇总该文件(Git服务器上的文件)的行尾总是LF,对于工作目录,除了text属性之外,还可以设置eol属性或core.eol配置变量
  • .gitattributes文件的一行中,一个属性(以text属性为例)可能有4种状态:
    • 未声明:通常不出现该属性即可;但是为了覆盖其他文件中的声明,也可以!text
    • 设置:text
    • 不设置:-text
    • 设置值:text=string
5.1.2.2 eol
  • 设置行末(end of line)字符
    • eol=lf:入库时将行尾规范为LF,检出时行尾不强制转换为CRLF
    • eol=crlf:入库时将行尾规范为LF,检出时将行尾转换为CRLF
5.1.2.3 diff
  • diff属性影响Git对文件前后差异的判断
    • diff:强制视为文本文件,即使它包含一些通常从不会出现在文本文件的字节值
    • !diff:表示为非文本文件,没有设置diff属性的路径会生成differ二进制文件(如果启用了二进制补丁,会生成二进制补丁)
    • 未定义:未指明diff属性的路径首先会检查其内容,如果它看起来像文本文件并且小于大文件阈值core.bigFileThreshold,则将其视为文本文件,否则将生成differ二进制文件

5.1.3 .gitattributes文件编写示例

  • 游戏引擎Spring开源仓库中的.gitattributes文件编写如下
1
2
3
4
5
6
7
8
# Declare files that will always have LF line endings on checkout.
*.cpp text eol=lf
*.h text eol=lf
*.c text eol=lf
*.hpp text eol=lf
*.cmake text eol=lf
*.sh text eol=lf
*.py text eol=lf
  • 另一个更详细的示例如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
*text=auto  
# 文件的行尾自动转换;如果是文本文件,则在文件入Git库时,行尾自动转换为LF;如果已经在入Git库中的文件的行尾是CRLF,则文件在入Git库时,不再转换为LF

*.txt text
# 对于.txt文件,标记为文本文件,并进行行尾规范化

*.jpg -text
# 对于`.jpg`文件,标记为非文本文件

*.vcproj text eol=crlf
# 对于.vcproj文件,标记为文本文件,在文件入Git库时进行规范化,行尾转换为LF,在检测到出工作目录时,行尾自动转换为GRLF

*.sh text eol=lf
# 对于sh文件,标记为文本文件,在文件入Git库时进行规范化,即行尾为LF。在检出到工作目录时,行尾也不会转换为CRLF(即保持LF)

*.py eol=lf
# 对于py文件,只针对工作目录中的文件,行尾为LF
  • 下面是Github官方提供的一个Windows平台的编写示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 设置默认行为,以防用户没有设置本地core.autocrlf的属性
* text=auto

# 明确指出哪些类型的文件是需要被规范化的文本文件,该类型文件在检出时会转换为本地行结束符
*.c text
*.h text

# 指令`.xxx text eol=crlf`使得Git始终在检出时把`.xxx`后缀文件的行结束符转换为CRLF,此命令应当用于即便在MacOSX或Linux上也必须保持CRLF结束符的文件
# 类比可以理解`text eol=lf`指令的作用,用于在Windos上也应当保持LF换行符的文件
*.sln text eol=crlf

# Git会理解指定文件不是文本,从而不会尝试更改这些文件;binary设置也是`-text -diff`的别名
*.png binary
*.jpg binary
  • 上面的示例中将图片标记为二进制文件,但Git仍然可以追踪文件的更改,只不过不会显示具体的行差异罢了

5.2 .gitattributes生效顺序

5.2.1 单个.gitattributes内的不同行命令的优先度

  • 同一个.gitattributes文件中,遵循覆盖原则,即后面的行会覆盖前面的设置,如果一个文件的某个属性被多次设置,则后设置的优先

5.2.2 多个.gitattributes文件间的优先度

这块内容的官方文档我没怎么看懂,说是和.gitignore类似,但又有几个地方有所不同;以下的内容来自别人的博客,仅供参考,若是我后续需要用到的话再详细实验考证一下

  • 在一个Git库中(的不同路径下)可以有多个.gitattributes文件,其之间不同的属性设置的优先级从高到低如下
    • /myproj/info/attributes文件
    • /myproj/my_path/.gitattributes文件
    • /myproj/.gitattributes文件

5.3 为已有仓库设置或重置.gitattributes

  • 若你的项目已经存在很久,但也想添加.gitattributes文件来统一行尾序列,跟随下列步骤
    • 添加相关的.gitattributes文件到项目
    • 运行git add .添加所有文件
    • 运行git commit -m "xxxxx"来保存本次更改
    • 运行git rm -rf --cached .删除所有文件,不包括.git目录
    • 运行git reset --hard xxxxx,恢复上一次的提交,这样就会得到正确的行尾序列

六、换行符问题

6.1 问题背景

6.1.1 历史遗留问题

参考此博客

  • CR代表回车,对应字符\r,而LF表示换行,对应字符\n
  • 不同操作系统在处理换行符时使用了不同的方案,Windows使用了CRLF,Unix阵营则使用LF,其中MacOS最初使用CR,后来到MacOSX改成了使用LF,与Linux保持了一致,虽然目前很多代码编辑器都支持自动识别和切换换行符风格,然而总有那么一些编辑器无法达到相应的兼容性

6.1.2 版本管理中的换行符问题

  • 比如当我们对一个项目的文件进行文本的修改后,然后执行git add .将修改添加到暂存区,这个操作有时候会受到如下图的警告,此处警告我们修改的文件中的LF将会被替换为CRLF(注:我使用的是Obsidian编辑器,这个逼玩意儿的换行符是LF,所以才会出现这个警告)

LF被替换为CRLF的警告.png

  • 针对Obsidian,我使用下列命令来让Git每次检出文件时不强制转换LFCRLF
1
2
# Because Obsidian uses LF though on Windows 
*.md text eol=lf

6.2 解决方法

参考官方文档

6.2.1 通过.gitattributes文件解决

  • 通过texteol属性的设置来控制换行符的问题

6.2.2 通过本地Git配置解决

  • Windows的Git默认的core.autocrlftrue,可以通过下面的指令查看此项设置
1
git config --list
  • Git可以在你提交时自动地把CRLF转换成LF,而在检出代码时把LF转换成CRLF,在Windows系统上可以用下面的指令把core.autocrlf设置成true来打开此项功能,反之设置false则关闭
1
git config --global core.autocrlf true
  • 若是在Windows上设置core.autocrlf=false的同时仓库里也没有配置.gitattributes的话,很容易引入CRLF或者混合换行符(Mixed Line Endings,即一个文件里既有 LF 又有CRLF)到版本库,这样就可能产生各种奇怪的问题
  • Linux或Mac系统使用LF作为行结束符,最好把core.autocrlf设置成input来告诉Git在提交时把CRLF转换成LF,签出时则不转换
1
git config --global core.autocrlf input

七、开源协议

关于各协议的细节,参考此处,以下仅介绍其中几个较为常用的协议

7.1 MIT

MIT又称X11协议,其唯一条件就是在修改后的代码或者发行包包含原作者的许可信息

7.2 BSD

BSD(Berkeley Software Distribution)允许使用者可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布,当你发布使用了BSD协议的代码,或则以BSD协议代码为基础做二次开发自己的产品时,需要满足三个条件:

  • 1、如果再发布的产品中包含源代码,则在源代码中必须带有原来代码中的BSD协议
  • 2、如果再发布的只是二进制类库/软件,则需要在类库/软件的文档和版权声明中包含原来代码中的BSD协议
  • 3、不可以用开源代码的作者/机构名字和原来产品的名字做市场推广

7.3 GPL

GPL(GNU General Public License)的出发点是代码的开源/免费使用和引用/修改/衍生代码的开源/免费使用,但不允许修改后和衍生的代码做为闭源的商业软件发布和销售

7.4 Apache

鼓励代码共享和最终原作者的著作权,同样允许源代码修改和再发布

  • 需要给代码的用户一份Apache Licence
  • 如果修改了代码,需要再被修改的文件中说明
  • 在衍生的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议,商标,专利声明和其他原来作者规定需要包含的说明
  • 如果再发布的产品中包含一个Notice文件,则在Notice文件中需要带有Apache Licence,你可以再Notice中增加自己的许可,但是不可以表现为对Apache Licence构成更改
  • Apache Licence也是对商业应用友好的许可,使用者也可以再需要的时候修改代码来满足并作为开源或商业产品发布/销售 这个协议的好处是
  • 永久权利:一旦被授权,永久拥有
  • 全球范围的权利:在一个国家获得授权,适用于所有国家。假如你在美国,许可是从印度授权的,也没有问题
  • 授权免费:无版税, 前期、后期均无任何费用
  • 授权无排他性:任何人都可以获得授权
  • 授权不可撤消:一旦获得授权,没有任何人可以取消,比如,你基于该产品代码开发了衍生产品,你不用担心会在某一天被禁止使用该代码

7.5 Creative Commons

Creative Commons许可协议大多是被使用于设计类而非开发类的工程上,一个CC许可协议具有四个基本部分,这几部分可单独或组合起来起作用

  • 1、署名 作品上必须附有作品的归属,如此之后,作品可以被修改,分发,复制和其它用途。
  • 2、相同方式共享 作品可以被修改、分发或其它操作,但所有的衍生品都要置于CC许可协议下。
  • 3、非商业用途 作品可以被修改、分发等等,但不能用于商业目的,但语言上对什么是”商业”的说明十分含糊不清(没有提供精确的定义),所以你可以在你的工程里对其进行说明,例如,有些人简单的解释”非商业”为不能出售这个作品;而另外一些人认为你甚至不能在有广告的网站上使用它们;还有些人认为”商业”仅仅指你用它获取利益
  • 4、禁止衍生作品
本文由作者按照 CC BY-NC-SA 4.0 进行授权