What

Git是一个开源的分布式版本控制系统

why

因为大家都在用

好吧,git还是能干点事的

  • 可以回退代码到之前的任意版本。毕竟程序员是一个相当玄学的工作,没准今天随便改两行,代码就跑不起来了,或者突然不需要某个功能了,这时候就可以回退版本

  • 便于多人协作。打个比方,两个人分别写一个项目的两个页面,如果没有好用的版本工具,最后上线时就要手动把代码复制粘贴到一起。过于机械化

所以,拯救CV战士,从Git开始

Need to Know

Git文件的四种状态

在学习具体的命令前,还是要先康康文件的状态

16343316cbad1533_tplv-t2oaga2asx-zoom-in-crop-mark_1304_0_0_0 (1)

  • Untracked: 未跟踪, 此文件在文件夹中, 但并没有加入到git库, 不参与版本控制. 通过git add 状态变为Staged.
  • Unmodify: 文件已经入库, 未修改, 即版本库中的文件快照内容与文件夹中完全一致. 这种类型的文件有两种去处, 如果它被修改, 而变为Modified. 如果使用git rm移出版本库, 则成为Untracked文件
  • Modified: 文件已修改, 仅仅是修改, 并没有进行其他的操作. 这个文件需要重新通过git add命令进入staged状态
  • Staged: 暂存状态. 执行git commit则将修改同步到库中, 这时库中的文件和本地文件又变为一致, 文件为Unmodified状态.

Git的几个分区

1634331848b2fff5_tplv-t2oaga2asx-zoom-in-crop-mark_1304_0_0_0

  • Workspace:工作区
  • Index / Stage:暂存区
  • Repository:仓库区(或本地仓库)
  • Remote:远程仓库

How

安装git

去官网下载:https://git-scm.com/downloads (opens new window),下载完成后,一路next安装即可

安装完成后,在开始菜单里找到 Git -> Git Bash,蹦出一个类似命令行窗口的东西,就说明Git安装成功! 还需要最后一步设置,在命令行输入:

1
2
git config --global user.name "Your Name"
git config --global user.email "email@example.com"

因为Git是分布式版本控制系统,所以,每个机器都必须自报家门,也需要告诉别人这个commit是谁提交的

你可以使用下面的命令,查看你的配置

1
git config --global --list

创建版本库

也不一定必须在空目录下创建Git仓库,选择一个已经有东西的目录直接git init也是可以的。

1
2
3
4
5
6
7
8
# 创建仓库
mkdir <仓库name>

# 进入仓库
cd <仓库name>

# 此命令用于显示当前目录
pwd
1
2
3
4
5
6
7
8
# 在当前目录新建一个Git代码库(截图用的是这个命令)
$ git init

# 新建一个目录,将其初始化为Git代码库
$ git init [project-name]

# 下载一个项目和它的整个代码历史
$ git clone [url]

image-20220430230829323

查看信息

在学习具体的命令前,还是需要知道怎么查看仓库的状态的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 显示有变更的文件
git status

# 显示当前分支的版本历史
git log

# 显示暂存区和工作区的差异
git diff

# 显示当前分支的最近几次提交
git reflog

# 显示指定文件的修改历史
$ git blame [file]

image-20220430230641778

image-20220430230654421

image-20220503144856485

提交文件

在仓库目录下放入文件,如新建一个test.txt文件,然后使用git add test.txt命令告诉Git,把文件添加到缓存区,然后使用git commit -m "提交描述"告诉Git,把文件提交到仓库。

1
2
3
4
5
6
7
8
9
10
# 缓存区也叫暂存区
# 添加指定文件或文件夹到缓存区,文件需添加后缀
git add <文件或文件夹name> # 单个文件
git add <文件或文件夹name> <文件或文件夹name> # 多个文件

# 或 全部文件同时添加到缓存区
git add .

# 把文件从缓存区提交至仓库
git commit -m "提交描述"

为什么需要addcommit,我觉得这个解释还是很合理的

image-20220430232145344

我暂时能想到的应用场景是:因为git commit只会提交处于暂存区的文件,如果某次提交时突然不想提交某个文件了,就可以把这个文件从暂存区中移除,这样本次提交就可以不提交某个文件了

image-20220501213311125

忽略文件

除了上面列举出的四个git文件状态,git文件其实还有一种状态

  • 不受版本控制的 untracked 状态

可以使用gitignore文件或者git update-index --assume-unchanged <files>命令让某个文件被忽视

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 配置.gitignore文件
# 空行或者以 # 开头的行都会被 Git 忽略。一般空行用于可读性的分隔
# 以斜杠 / 结尾表示忽略目录
# 以 / 开头表示根目录下的文件或目录
# 以叹号 ! 表示不忽略匹配到的文件或目录

# 本行为注释
.idea/ # 忽略仓库中所有.idea 目录
/.idea/ # 忽略仓库中根目录下的.idea 目录
/.settings # 忽略仓库中根目录下的 .settings 文件或目录
~'$'*.docx # office 打开时生成的临时文件

!etc/eclipse/.checkstyle # 不忽略 .checkstyle 文件或目录

或者使用

1
2
3
4
# 开启忽视
git update-index --assume-unchanged <files>
# 取消忽视
git update-index --no-assume-unchanged <files>

效果如下:

image-20220501212732662

撤销修改

方法很多,随便选,反正到时用了GUI也用不上这么多花里胡哨的了

可以使用checkout

1
2
3
4
5
6
7
8
# 丢弃工作区的修改,并用最近一次的commit内容还原到当前工作区(对文件中内容的操作,无法对添加文件、删除文件起作用)
git checkout – <file_name>

# 将指定commit提交的内容(HEAD^表示上一个版本)还原到当前工作区
git checkout HEAD^ – <file_name>

# 将指定分支的指定提交内容还原到当前分支工作区
git checkout <branch_name> – <file_name>

或者restore

1
2
3
4
# 将暂存区的修改重新放回工作区(包括对文件自身的操作,如添加文件、删除文件)
git restore --staged <file_name>
# 将暂存区的修改重新放回工作区(包括对文件自身的操作,如添加文件、删除文件)
git restore <file_name> 丢弃工作区的修改(包括对文件自身的操作,如添加文件、删除文件)

或者reset

1
2
3
4
5
# 丢弃暂存区的修改,重新放回工作区,会将暂存区的内容和本地已提交的内容全部恢复到未暂存的状态,不影响原来本地文件(相当于撤销git add 操作,不影响上一次commit后对本地文件的修改) (包括对文件的操作,如添加文件、删除文件)
git reset HEAD <file_name>

# 清空暂存区,将已提交的内容的版本恢复到本地,本地的文件也将被恢复的版本替换(恢复到上一次commit后的状态,上一次commit后的修改也丢弃)
git reset –hard HEAD

看不懂?举几个例子吧

现在的文件和工作区是这样的

image-20220501221749645

改点内容但是没有add,这时候想要撤销工作区的修改,使用checkout

image-20220501223418134

image-20220501223621094

改了内容并add,想要取消add的状态,使用restore或者reset

image-20220501224305630

image-20220501225204311

版本回退

首先,Git必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交1094adb…(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100

一般使用reset回退到之前的版本

1
2
3
4
5
6
7
8
9
# 回退到上一个版本
git reset --hard HEAD^
# 跳转到指定版本 版本号使用git log查看
# 另外 回退后会找不到没有回退前的那个版本 可以使用git reflog查看所有的操作记录
git reset --hard <版本号前几位>

# --hard:不保存所有变更,所有文件都会回到没有变更时的状态
# --soft:保留变更且变更状态处于Staged,也就是工作区的文件还是在的
# --mixed:保留变更且变更状态处于Modiified,也就是工作区的文件还是在的

执行效果

image-20220502220553222

image-20220502220657222

image-20220502220739440

如图,softmixed会保留文件内容

image-20220502221456487

分支管理

创建分支

1
2
3
4
5
6
7
8
9
10
11
# 新建一个分支,但依然停留在当前分支
$ git branch [branch-name]

# 新建一个分支,并切换到该分支
$ git checkout -b [branch]

# 新建一个分支,指向指定commit
$ git branch [branch] [commit]

# 新建一个分支,与指定的远程分支建立追踪关系
$ git branch --track [branch] [remote-branch]

查看分支

1
2
3
4
5
6
7
8
# 列出所有本地分支
$ git branch

# 列出所有远程分支
$ git branch -r

# 列出所有本地分支和远程分支
$ git branch -a

切换分支

1
2
3
4
5
6
7
8
# 切换到指定分支,并更新工作区
$ git checkout [branch-name]

# 切换到上一个分支
$ git checkout -

# 建立追踪关系,在现有分支与指定的远程分支之间
$ git branch --set-upstream [branch] [remote-branch]

合并分支

1
2
3
4
5
# 合并指定分支到当前分支
$ git merge [branch]

# 选择一个commit,合并进当前分支
$ git cherry-pick [commit]

删除分支

1
2
3
4
5
6
# 删除分支
$ git branch -d [branch-name]

# 删除远程分支
$ git push origin --delete [branch-name]
$ git branch -dr [remote/branch]

效果如下

image-20220502223845048

image-20220502223910587

单人远程同步

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
# 下载远程仓库的所有变动
git fetch [remote]

# 显示所有远程仓库
git remote -v

# 显示某个远程仓库的信息
git remote show [remote]

# 增加一个新的远程仓库,并命名
git remote add [shortname] [url]

# 取回远程仓库的变化,并与本地分支合并
git pull [remote] [branch]

# 上传本地指定分支到远程仓库
git push [remote] [branch]

# 强行推送当前分支到远程仓库,即使有冲突
git push [remote] --force

# 推送所有分支到远程仓库
git push [remote] --all

# 取消和远程仓库的关联
git remote rm origin

用的时候,在远端创建git仓库

image-20220502233952125

然后运行下面的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
# create a new repository on the command line
echo "# test-rua" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/sliyoxn/test-rua.git
git push -u origin main

# push an existing repository from the command line
git remote add origin https://github.com/sliyoxn/test-rua.git
git branch -M main
git push -u origin main

这里不知道为什么我的浏览器一直没反应…我只能直接用GUI

image-20220502234158396

难搞

image-20220502234216451

image-20220502234254189

之后,修改并提交即可

多人远程同步

开个小号开个仓库

image-20220502234414555

邀请一下

image-20220502234515522

然后clone

image-20220502234657665

写代码并按之前的操作提交即可

image-20220502234735650

Else

git stash

参考:https://jasonkayzk.github.io/2020/05/03/Git-Stash%E7%94%A8%E6%B3%95%E6%80%BB%E7%BB%93/

git stash命令的作用就是将目前还不想提交的但是已经修改的内容进行保存至堆栈中,后续可以在某个分支上恢复出堆栈中的内容。

使用场景

  • 当正在dev分支上开发某个项目,这时项目中出现一个bug,需要紧急修复,但是正在开发的内容只是完成一半,还不想提交,这时可以用git stash命令将修改的内容保存至堆栈区,然后顺利切换到hotfix分支进行bug修复,修复完成后,再次切回到dev分支,从堆栈中恢复刚刚保存的内容。
  • 由于疏忽,本应该在dev分支开发的内容,却在master上进行了开发,需要重新切回到dev分支上进行开发,可以用git stash将内容保存至堆栈中,切回到dev分支后,再次恢复内容即可。

当然,热修复时不创建新分支也是理论可行的,虽然修复bug一般是要弄一个bugfix分支的

不切换分支时的使用

image-20220503151319687

切换分支时的使用

image-20220503151854369

其实就是这个东西

image-20220503151926912

image-20220503152016938

merge和rebase的区别

参考https://juejin.cn/post/6844903603694469134

看图通俗易懂

git merge 有如下特点:

  • 只处理一次冲突
  • 引入了一次合并的历史记录,合并后的所有 commit 会按照提交时间从旧到新排列
  • 所有的过程信息更多,可能会提高之后查找问题的难度

16342fbc3161f98e_tplv-t2oaga2asx-zoom-in-crop-mark_1304_0_0_0

git rebase 有如下特点:

  • 改变当前分支从 master 上拉出分支的位置
  • 没有多余的合并历史的记录,且合并后的 commit 顺序不一定按照 commit 的提交时间排列
  • 可能会多次解决同一个地方的冲突(有 squash 来解决)
  • 更清爽一些,master 分支上每个 commit 点都是相对独立完整的功能单元

16342fc20a6c6c8f_tplv-t2oaga2asx-zoom-in-crop-mark_1304_0_0_0 (1)

git工作流

参考:https://gitbook.tw/chapters/gitflow/why-need-git-flow

根据 Git Flow 的建议,主要的分支有masterdevelophotfixrelease 以及 feature 这五种分支,各种分支负责不同的功能。其中 Master 以及 Develop 这两个分支又被称做长期分支,因为他们会一直存活在整个Git Flow 里,而其它的分支大多会因任务结束而被删除。

git flow

Master 分支
主要是用来放稳定、随时可上线的版本。这个分支的来源只能从别的分支合并过来,开发者不会直接 Commit 到这个分支。

Develop 分支
这个分支主要是所有开发的基础分支,当要新增功能的时候,所有的 Feature 分支都是从这个分支切出去的。而 Feature 分支的功能完成后,也都会合并回来这个分支。

Hotfix 分支
当线上产品发生紧急问题的时候,会从 Master 分支开一个 Hotfix 分支出来进行修复,Hotfix 分支修复完成之后,会合并回 Master 分支,也同时会合并一份到 Develop 分支。

1
2
3
4
5
# 为什么要合并回 Develop 分支
如果不这么做,等到时候 Develop 分支完成并且合并回 Master 分支的时候,那个问题就又再次出现了。

# 那为什么一开始不从 Develop 分支切出来修
因为 Develop 分支的功能可能尚在开发中,这时候硬是要从这里切出去修再合并回 Master 分支,只会造成更大的灾难。

Release 分支
当认为 Develop 分支够成熟了,就可以把 Develop 分支合并到 Release 分支,在这边进行算是上线前的最后测试。测试完成后,Release 分支将会同时合并到 Master 以及 Develop 这两个分支上。 Master 分支是上线版本,而合并回 Develop 分支的目的,是因为可能在 Release 分支上还会测到并修正一些问题,所以需要跟 Develop 分支同步,免得之后的版本又再度出现同样的问题。

Feature 分支
当要开始新增功能的时候,就是使用 Feature 分支的时候了。 Feature 分支都是从 Develop 分支来的,完成之后会再并回 Develop 分支。