【通用开发】Git基础与进阶

本文介绍了工作过程中的Git工具使用总结
Git的各个库的组成部分主要分为以下几个区域:

区域的含义:
- 工作区域(Working Directory)就是你平时存放项目代码的地方。
- 暂存区域(Stage)用于临时存放你的改动,事实上它只是一个文件,保存即将提交的文件列表信息。
- Git 仓库(Repository)就是安全存放数据的位置,这里边有你提交的所有版本的数据。其中,HEAD 指向最新放入仓库的版本(这第三棵树,确切的说,应该是 Git 仓库中 HEAD 指向的版本)。
- remote就是远端的代码仓,存放在线的公共的代码。
基础提交代码三大步:
git add .
# 添加所有修改到暂存区
git commit -m "message"
# 提交修改到仓库区域
git push origin main
# 将提交的修改上传到git服务器仓库
两种拉取代码的方式
http克隆
直接通过仓库的https链接就可以拉取下载。
git clone https://github.com/stepheneasyshot/stepheneasyshot.github.io.git
ssh克隆
生成本地key
打开cmd命令行,输入以下命令:
C:\Users\stephen\Desktop>ssh-keygen -t rsa -b 4096 -C "zhanfeng990927@gmail.com"
Generating public/private rsa key pair.
Enter file in which to save the key (C:\Users\stephen/.ssh/id_rsa):
Created directory 'C:\\Users\\stephen/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in C:\Users\stephen/.ssh/id_rsa
Your public key has been saved in C:\Users\stephen/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:XXXXXXXXXX/4pbXXXXXXXXXXXXXXXXX zhanfeng990927@gmail.com
The key's randomart image is:
+---[RSA 4096]----+
| XXXXXX |
| XXXXXXXXX |
| XXXXX |
| XXXXXX |
| XXXXXXXXXX |
| XXXXXXXXXX |
| XXXXXXXXXXXX |
| XXXXXXXXXX |
| XXXXXXXXXXX |
+----[SHA256]-----+
Enter passphrase这两步是提示输入密码,建议不要设置,直接生成。
完毕后在C盘的用户文件夹下应该会生成一个 .ssh 文件夹,可以在 .ssh 目录下看到两个文件:id_rsa 和 id_rsa.pub
其中的pub公钥就是需要上传到git服务器上做验证的文件。
上传key到git服务器
生成SSH Key后,需要将公钥添加到远程Git仓库中。使用cat命令查看公钥内容,Windows上应该直接使用记事本打开即可显示:
cat ~/.ssh/id_rsa.pub
一般格式如下:
ssh-rsa XXXXXX...lrw== zhanfeng990927@gmail.com
然后,复制这串公钥内容。 登录到远程Git仓库,找到SSH Key配置页面,将公钥粘贴到相应位置并保存。
验证并拉取代码
打开git bash,输入ssh -T git@github.com做初次验证,然后就可以通过ssh的方式拉取github上的代码了:
~/Desktop:$ ssh -T git@github.com
The authenticity of host 'github.com (20.205.243.166)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? y
Please type 'yes', 'no' or the fingerprint: yes
Warning: Permanently added 'github.com' (ED25519) to the list of known hosts.
Hi stepheneasyshot! You've successfully authenticated, but GitHub does not provide shell access.
~/Desktop:$ git clone git@github.com:stepheneasyshot/stepheneasyshot.github.io.git
Cloning into 'stepheneasyshot.github.io'...
Git文件状态
通常我们需要查看一个文件的状态,使用git status
Changes not staged for commit 表示得大概就是工作区有该内容,但是缓存区没有,需要我们git add.
Changes to be committed 一般而言,这个时候,文件放在缓存区了,我们需要git commit.
nothing to commit, working tree clean 这个时候,我们将本地的代码推送到远端即可。
git配置
列出当前配置
git config --list
列出当前Repository配置
git config --local --list
列出全局配置
git config --global --list
列出系统配置
git config --system --list
配置用户名
git config --global user.name "your name"
配置用户邮箱
git config --global user.email "youremail@github.com"
分支管理
查看本地分支
git branch
查看远程分支
git branch -r
查看本地和远程分支
git branch -a
从当前分支,切换到其他分支
git checkout <branch-name>
创建并切换到新建分支
git checkout -b <branch-name>
删除分支
git branch -d <branch-name>
// 若有未提交的代码,仍然强行删除刻可以使用 D
当前分支与指定分支合并
git merge <branch-name>
查看哪些分支已经合并到当前分支
git branch --merged
查看哪些分支没有合并到当前分支
git branch --no-merged
查看各个分支最后一个提交对象的信息
git branch -v
删除远程分支
git push origin -d <branch-name>
重命名分支
git branch -m <oldbranch-name> <newbranch-name>
拉取远程分支并创建本地分支
git checkout -b 本地分支名x origin/远程分支名x
// 另外一种方式,也可以完成这个操作。
git fetch origin <branch-name>:<local-branch-name>
fetch推荐写法
git fetch origin <branch-name>:<local-branch-name>
一般而言,这个origin是远程主机名,一般默认就是origin。
branch-name 你要拉取的分支
local-branch-name 通常而言,就是你本地新建一个新分支,将origin下的某个分支代码下载到本地分支。
撤销操作
撤销工作区修改
git checkout -- file
暂存区文件撤销 (不覆盖工作区)
git reset HEAD
版本回退
git reset --(soft | mixed | hard ) < HEAD ~(num) >
可以使用git log 找到commitid,以精确还原
比较差异
比较工作区与缓存区
git diff
比较缓存区与本地库最近一次commit内容
git diff -- cached
比较工作区与本地最近一次commit内容
git diff HEAD
比较两个commit之间差异
git diff <commit ID> <commit ID>
一般分支命名
master分支
主分支,用于部署生产环境的分支,确保稳定性。 master分支一般由develop以及hotfix分支合并,任何情况下都不能直接修改代码。
develop 分支
develop为开发分支,通常情况下,保存最新完成以及bug修复后的代码。 开发新功能时,feature分支都是基于develop分支下创建的。
feature分支
开发新功能,基本上以develop为基础创建feature分支。 分支命名:feature/ 开头的为特性分支, 命名规则: feature/user_module、 feature/cart_module。
release分支
release 为预上线分支,发布提测阶段,会release分支代码为基准提测。
hotfix分支
分支命名:hotfix/ 开头的为修复分支,它的命名规则与 feature 分支类似。线上出现紧急问题时,需要及时修复,以master分支为基线,创建hotfix分支,修复完成后,需要合并到master分支和develop分支。
.gitignore文件配置
这个文件的作用,会去忽略一些不需要纳入Git管理这种,我们也不希望出现在未跟踪文件列表。
一般配置方法:
# 井号,此行是注释 会被Git忽略
# 忽略 node_modules/ 目录下所有的文件
node_modules/
# 忽略所有.vscode结尾的文件
.vscode
# 忽略所有.md结尾的文件
*.md
# 但README.md 除外
!README.md
# 会忽略 doc/something.txt 但不会忽略doc/images/arch.txt
doc/*.txt
# 忽略 doc/ 目录下所有扩展名为txt文件
doc/**/*.txt
增加效率的一些操作
git stash
某一天你正在 feature 分支开发新需求,突然产品经理跑过来说线上有bug,必须马上修复。而此时你的功能开发到一半,于是你急忙想切到 master 分支,然后你就会看到以下报错:
error: Your local changes to the following files would be overwritten by checkout:
src/entries/index/config/group.js
Please commit your changes or stash them before youswitch branches.
Aborting
因为当前有文件更改了,需要提交commit保持工作区干净才能切分支。由于情况紧急,你只有急忙 commit 上去,commit 信息也随便写了个“暂存代码”,于是该分支提交记录就留了一条黑历史…
不想随便写一个commit,又要切代码回去,就可以用git stash这个命令。 代码修改会被暂存下来,当从主分支修完bug切回来时,可以使用
git stash apply
来恢复代码。
相关命令如下:
# 保存当前未commit的代码
git stash
# 保存当前未commit的代码并添加备注
git stash save "备注的内容"
# 列出stash的所有记录
git stash list
# 删除stash的所有记录
git stash clear
# 应用最近一次的stash
git stash apply
# 应用最近一次的stash,随后删除该记录
git stash pop
# 删除最近的一次stash
git stash drop
可以生成多条stash,并使用git stash list来查看。
$ git stash list
stash@{0}: WIP on ...
stash@{1}: WIP on ...
stash@{2}: On ...
应用第二条记录:
$ git stash apply stash@{1}
pop,drop 同理。
git reset
有soft和hard两种。
git reset –hard commitId
将git头部回溯到这笔提交,这笔之后的所有本地修改全部删除,新建的文件貌似不会自动删除。
依次提交a,b,c,
使用hard模式回溯到a,
那么本地b,c的内容将被删除
git reset –soft commitId
将git的头部回溯到这一笔提交,之后的所有变更修改都会被保存下来放到暂存区,并当作下一笔的提交内容。
依次提交a,b,c,使用soft模式回溯,会将b,c的修改保留,在下一次commit的时候将合并到一起。
git cherry-pick
将一个分支的某些提交,复制到另一个分支上,比如release分支上修复的某一些bug,在开发dev分支上也需要同步上去。 这时候就可以使用这个命令。
单个提交举例
如果要把分支 b 的 test 提交给复制到分支 a 上。 先在b分支上使用git log,获取 test 这笔提交的commitHash值,切到分支a,使用:
git cherry-pick commitHash
就可以同步分支b的这一笔提交。
一次转移多个提交
git cherry-pick commit1 commit2
上面的命令将 commit1 和 commit2 两个提交应用到当前分支。
多个连续的commit,也可区间复制:
git cherry-pick commit1^..commit2
上面的命令将 commit1 到 commit2 这个区间的 commit 都应用到当前分支(包含commit1、commit2),commit1 是最早的提交。
cherry-pick冲突
如果分支b有提交1,2,3,4,5需要同步到分支a。 1,2,3顺利合并了,但是复制到第4个提交时,与分支a的本地提交修改的内容有冲突了,这时候需要解决冲突。
有三种方式
放弃cherry-pick
git cherry-pick --abort
这个操作会将已经合入的123也给回退掉,就像什么都没有发生过。
退出cherry-pick
git cherry-pick --quit
保留已经合入的1,2,3笔提交,还有正卡在冲突处的第4笔提交,退出合入过程,这时候第5笔就相当于放弃了。
解冲突后继续合入
git cherry-pick --continue
在手动解完冲突并commit提交后,执行continue命令,会把未合入的第5笔提交也同步进来。
git revert
有提交1,2,3,现在发现提交2会引起严重问题,需要将这一笔撤销掉。可以使用reset命令,但是如果使用reset –hard,会将提交3也回退,需要重新提一次第3笔。
这时候可以使用revert精准回退某一笔提交:
git revert commitHash
输入完后push上去,可以只回退提交2的内容。
包含merge节点的revert回退
如果有提交1,2,3,三笔提交,其中提交3是dev分支合并到主分支上的信息。
这时候直接revert掉2,git会搞不清楚是回退主分支上的2,还是回退掉dev分支上的2,执行不成功。
需要使用 -m 手动指定保留的分支,另一条分支上的2就会被回退。
-m 后面要跟一个 parent number 标识出”主线”,一般使用 1 保留主分支代码。
git revert -m 1 <commitHash>
revert了主分支,dev修好之后合并失效
情景和上面类似,但是把master分支上有问题的提交给revert掉,
如下图,有问题的提交是 b 这一笔,在master上revert掉之后,在dev上修复了这个有问题的提交,准备再次往主分支合并,这时候发现dev上的 b 提交没有同步到master,因为master上已经有一笔 b 的合入记录,在dev修改完毕准备合入时,git一比较,这两笔相同的commitHash,就不会合入dev上 b 的提交。 
解决方案
使用reset将master回滚出问题之前,也就是 a 这一笔提交。这样master上面就不会有b的残留记录了。
然后把修复完毕的dev分支合并进来。
git reflog
reflog可以找到所有的操作记录。用于误删或者回退过多的场景。
即使在使用了 git reset --hard 或 git branch -D 等命令后,仍然可以通过 reflog 找到之前的状态。
如果有提交a,b,c。其中 c 是你提交的有问题的一笔,需要reset掉,但是看错了commitHash值,将别人提交的 b 都给回退掉了,现在使用git log回溯,git记录上只有a这笔提交了。
这时候可以使用git reflog,它记录了所有的操作记录,记下需要恢复的commitID,就可以还原回来被误删的 b 这笔提交了。
设置git的短命令
方式一
git config --global alias.ps push
方式二 打开用户目录下的 .gitconfig 文件,写入:
[alias]
co = checkout
ps = push
pl = pull
mer = merge --no-ff
cp = cherry-pick
就可以使用短命令来进行git操作了。
例如:
git cp <commitHash>
git rebase
git rebase最大的作用就是让提交记录更加简洁。
分支变基
现在有这一个场景:

master分支上A之后拉出一条开发分支,而后一个同事提交了B。
你在dev分支上提交了C和D,现在想把dev合并到主分支,使用merge的话,会多出一条空白的merge提交信息。
这时候可以使用
git checkout feature
git rebase master
//这两条命令等价于git rebase master feature
相当于让dev分支的基底从提交A的master开始,变成了从提交B开始,后面提交的C和D就是顺序的提交了。
整个提交记录则是ABCD连续的,合并到主分支上时不会产生merge信息。
同样适用于同一分支上不同开发的提交信息简化。
没有merge信息有好有坏吧,虽然看起来简洁,也无法回溯合代码的历史了。
重塑提交历史
写法如下:
git rebase -i [地址引用]
可以让你把最近的几笔提交信息进行操作,可以提交的调整顺序,合并,删除不想要的提交。即可以将多笔记录合并成一个。
例如:
git rebase -i HEAD~4
就可以列出HEAD开始往前四笔提交,供你编辑。
推荐场景
不同公司,不同情况有不同使用场景,不过大部分情况推荐如下:
单人开发的时候,拉公共分支最新代码的时候使用rebase,也就是git pull -r或git pull –rebase。这样的好处很明显,提交记录会比较简洁。但有个缺点就是rebase以后我就不知道我的当前分支最早是从哪个分支拉出来的了,因为基底变了嘛,所以看个人需求了。
多人开发,往公共分支上合代码的时候,使用merge。如果使用rebase,那么其他开发人员想看主分支的历史,就不是原来的历史了,历史已经被你篡改了。举个例子解释下,比如张三和李四从共同的节点拉出来开发,张三先开发完提交了两次然后merge上去了,李四后来开发完如果rebase上去(注意李四需要切换到自己本地的主分支,假设先pull了张三的最新改动下来,然后执行<git rebase 李四的开发分支>,然后再git push到远端),则李四的新提交变成了张三的新提交的新基底,本来李四的提交是最新的,结果最新的提交显示反而是张三的,就乱套了。
正因如此,大部分公司其实会禁用rebase,不管是拉代码还是push代码统一都使用merge,虽然会多出无意义的一条提交记录“Merge … to …”,但至少能清楚地知道主线上谁合了的代码以及他们合代码的时间先后顺序。