零、什么是Git
-
分布式版本控制工具
-
源代码管理器SCM
-
版本控制系统VCS
-
官网:https://git-scm.com/
-
官方文档:https://git-scm.com/docs
-
Pro Git:https://git-scm.com/book/en/v2
-
GitHub Cheat Sheet:https://github.github.com/training-kit/
-
Visual Git Cheat Sheet:https://ndpsoftware.com/git-cheatsheet.html (非常清晰,强烈推荐)
-
Visualizing Git Concepts with D3:https://onlywei.github.io/explain-git-with-d3/
-
学习网站:https://www.git-tower.com/learn/
一、Git原理
三大区域
工作流
重要概念
- 分区:工作区、暂存区、版本库
- 文件分类:tracked,untracked,ignored;staged,unstaged
- 会忽略的文件:.gitignore文件中指定的文件、空目录
数据管理机制
- SVN
- 增量式,每个版本保存变化的部分
- Git
- 快照流,每个版本保存所有文件的一个快照,如果文件没有修改,文件指针会指向上一个版本的对应文件
哈希算法
- 是一类加密算法:明文->密文
- 同种哈希算法,不管输入量多大,加密的结果长度一致
- 同种哈希算法,只要输入有一点变化,加密结果就会有很大的变化
- 哈希算法不可逆
- 哈希算法可用于文件校验
- Git底层使用SHA-1算法,40个16进制位
提交的表示
- 散列id,相对于HEAD
- HEAD始终指向当前分支的最新提交
- HEAD^ HEAD~ HEAD^~ HEAD^1 HEAD~1 HEAD^1~1 均表示HEAD的第一个父提交
- HEAD^m 控制回退到哪个分支(当HEAD指向的当前提交由多个分支合并而来时)
- HEAD~n 控制回退的步数
- HEAD^2表示 HEAD的第二个父提交
- HEAD~2表示HEAD的第一个父提交的第一个父提交
二、安装Git
Windows
- 官网:https://git-scm.com/
- 本文所有操作基于Git 2.24.0
- 检查是否安装成功
git
# or
git --version
Linux
sudo apt install git-all
三、Git配置
签名
签名是设置提交时的作者信息,包括用户名和邮箱
- 项目级签名
git config --local user.name "username"
git config --local user.email "useremail"
- 用户级签名(推荐)
git config --global user.name "username"
git config --global user.email "useremail"
- 系统级签名
git config --system user.name "username"
git config --system user.email "useremail"
设置color.ui
git config --global color.ui auto
四、GitHub和GitLab
- 代码托管中心
在GitHub上新建仓库
在GitLab上新建项目
SSH连接至GitHub
参考官方文档指引
Git Bash中输入ssh-keygen -t rsa -C “your_email@example.com”,一路默认即可
打开用户目录下的.ssh文件夹中的id_rsa.pub,全部复制。注:id_rsa 文件是私钥, id_rsa.pub 是公钥。
进入Github的Account Settings,左边选择SSH and GPG Keys,New SSH Key,title随便填,粘贴上一步复制的公钥,确认。
Git bash上输入ssh -T git@github.com
五、Git命令
0.常用命令
git init
git add
git commit
git status
git push
git pull
git merge:合并,提交是菱形的,会产生一个commit记录
git rebase: 合并,提交是线性的,本地提交记录的hash会变
git fetch
git branch
git checkout
git reset
git stash/apply
git remote add origin xxxx
git clone
1.基本操作
查看帮助
# 在线文档
git help subcommand
git --help subcommand
git subcommand --help
# 本地简介
git subcommand -h
创建仓库
-
cd到需要创建代码库的文件夹
-
在当前目录新建仓库,在当前目录下多出一个.git的隐藏文件夹
git init
从工作区添加到暂存区
git add 文件名
git add . # 将所有修改或删除的tracked file和新添加的untracked file添加到暂存区,忽略.gitignore指定的文件
git add * # 将所有修改tracked file和新添加的untracked file添加到暂存区,注意不包括删除的文件,.gitignore中的文件也会被添加(一般会有提示,需要加-f)
从暂存区添加到版本库
git commit # 填写commit message,保存退出
git commit -m "short commit message"
从工作区直接添加到版本库
- -a 只会add修改或删除的tracked file,不会add新增加的untracked file
git commit -a -m "commit message"
修改最后一次提交
- 暂存区没有内容时,用下面的命令可以修改提交信息,本质上是创建一次新的提交,git log只能看到修改后的提交,git reflog则可以查看这两次提交。
git commit --amend
-
上面这条命令更大的用处在于修复最近的一次提交,比如提交后忘了添加某个文件,或者有一处地方忘了修改,可以在修改后提交到暂存区,再执行上面的命令,这样git log就不会出现两次提交,参考博客
-
需要注意,如果错误的提交已经推送到远程仓库,修改后再push必须-f,否则出错
-
强行推送后远程分支也是只有修改后的提交
查看文件状态
- 能查看工作区和暂存区的文件状态,还会有各种命令提示👍
git status
比较文件
- 将工作区的文件和版本库的文件进行比较
# Changes in the working tree not yet staged for the next commit.
git diff
git diff 【文件名】
- 将暂存区的文件和版本库的文件进行比较
# Changes between the index and your last commit; what you would be committing if you run "git commit" without "-a" option.
git diff --cached
# or
git diff --staged
- 太难用了,还是用IDE更加直观。。。
重命名文件
git mv 原文件名 新文件名
git mv README.md README
# 相当于
mv README.md README
git rm README.md
git add README
删除文件
- 删除untracked file,直接rm
rm 文件名
- 删除版本库的文件,工作区不保留该文件
# 如果用rm,这个修改不会被stage,需要手动add再commit
# 一般用git rm,删除后自动stage
git rm 文件名
# 删除后要commit
git commit
- 删除版本库的文件,但工作区保留该文件
# 执行下面的命令会把文件变为untracked,置于工作区,并从删除索引
git rm --cached 文件名
# 删除后要commit
git commit
- 删除暂存区的文件,必须加-f强制删除,或者–cached保留在工作区
git rm -f 文件名 # 强制删除
# or
git rm --cached 文件名 # 保留在工作区
# 删除后要commit
git commit
撤销删除
- untracked file删除了就没法用git撤销,这里讲的都是tracked file
# 如果只是rm,没有暂存
git restore 文件名
# or
git checkout -- 文件名
# 如果rm后已经add
git restore --staged 文件名
# or
git reset HEAD 文件名 # 将暂存区文件撤销到工作区
# 如果已经commit,可以到上一个版本
git reset --hard HEAD^
撤销修改
- 工作区的tracked file撤销修改(没有git add)
git restore 文件名
# or
git checkout -- 文件名
- 暂存区的tracked file撤销修改(没有git commit)
git restore --staged 文件名
# or
git reset HEAD 文件名 # 将暂存区文件撤销到工作区
git reset HEAD # 取消多个已经缓存的文件
撤销提交
- 撤销某次提交,但保留该提交后面的所有提交
- 如果撤销提交的文件在后面的提交被修改,则可能会有冲突
- 参考链接
git revert commitid
git revert -n commitid # 不要自动commit,需要手动commit
查看提交历史
git log # 相当于 git log HEAD
git log --oneline
git log --pretty=oneline
# (git config format.pretty oneline)
# 查看某个分支提交历史
git log master
git log dev
查看提交详情
git show HEAD
git show commitid
版本穿梭
- 恢复历史版本(移动HEAD指针)
git reset --hard commitid # 回退到某次提交
git reset --hard HEAD # 将暂存区文件撤销到工作区
git reset --hard HEAD^ # 回退一个提交
git reset --hard HEAD^^ # 回退两个提交
git reset --hard HEAD~n # 回退n个提交
git reset --soft # 仅在本地库移动HEAD指针
git reset --mixed # 在本地库移动HEAD指针,重置暂存区
git reset --hard # 在本地库移动HEAD指针,重置暂存区和工作区
-
介绍
git reset
的深度好文 -
回退到任意一次提交(该记录有时限,且只在本地)
git reflog
git reset --hard commitid
- 本地回退之后,远程库也要同步更新,因为本地库HEAD指向的版本比远程库的要旧,必须加-f,否则报错
git push -f 【origin master】 # 如果设置了-u可以省略【origin master】
游离穿梭
- 穿越到过去的某个版本,不会影响git log
- 穿越后如果做出了修改,一定要commit,否则回不到现在
- 但是即使commit了,后续的版本也是无感知的
- 所以最好是开辟一个分支(平行宇宙)后再做修改
git checkout commitid
git checkout -b 新分支
# or
git switch -c 新分支
各种后悔药
- 参考深度好文
- 还原00:工作区中未加到暂存区和版本库的文件,还原今天所做的修改
- 尝试下Ctrl+z吧,不行就找找自动保存的缓存文件,看看能不能找到之前版本
- 还原01:工作区中未加到暂存区和版本库的文件,执行了
git add
操作
- 直接使用
git restore --staged file_name
命令,如果版本不支持则使用git rm --cached file_name
- 还原02:版本库中的文件,修改或删除后未执行 git add 操作
- 直接使用
git restore file_name
命令,如果版本不支持则使用git checkout -- file_name
- 还原03:版本库中的文件,修改或删除后执行了 git add 操作
- 直接使用
git restore --staged file_name
命令,按<还原02>
情况处理- 还原04:版本库中的文件,修改或删除后执行了 git add、git commit 操作
- 直接使用
git reset HEAD^
命令,按<还原02>
情况处理,或者使用git reset --soft HEAD^
命令,按<还原03>
情况处理- 还原05:版本库中的文件,修改或删除后执行了
git add
、git commit
、git push
操作
- 先按照
<还原04>
情况处理,然后使用git push -f
命令- 还原06:两次
git commit
之后产生两条日志,只还原第一次提交
- 使用
git revert HEAD^
命令,解决冲突后提交,revert 后面跟具体的commit id
也可以
2.分支
本质:创建和移动指针
创建和切换分支
- 新建分支
git branch branchname # 如果没有指定提交,就从HEAD开始分支
git branch branchname [commitid]
- 切换到指定分支
git checkout branchname
- 新建分支并切过去
git checkout -b branchname
- 假设master和dev一致,然后在dev分支做了修改,但没有add或commit,切换到master分支后,仍然能看到在dev分支所做的修改
- 假设dev分支领先master分支n个提交,然后在dev分支做了修改并且add,但是没有commit,这是切换到master分支会报错
删除分支
- 删除已合并分支
git branch -d branchname
- 删除未合并分支
git branch -D branchname # 注意删除未合并分支要用-D,否则报错
$ git branch -d dev
error: The branch ‘dev’ is not fully merged.
If you are sure you want to delete it, run ‘git branch -D dev’.
- 删除远程分支(建议界面操作)
git push <远程仓库名> -d 分支名
git push origin -d 分支名
git push origin -d dev
# or
git push origin :分支名 # 把远程分支变为空,也就删除了
查看分支
- 列出本地分支
git branch
- 列出远程分支
git branch -r
- 列出本地分支和远程分支
git branch -a
- 查看分支提交图
git log --graph
git log --graph --pretty=oneline --abbrey-commit
比较分支
git diff source_branch target_branch
重命名分支
git branch -m master master2
合并分支
- 将指定分支合并到当前分支,默认使用fast forward,两个分支归于同一个commit
git merge branchname
# 如果有冲突,编辑文件,保存退出
git add 文件名 # 告知git,冲突已解决
git commit -m "commit message" # 解决冲突日志,注意这里不能带文件名
- 这里可以看到合并冲突后master分支领先于dev分支
清理分支
# 查看无效的远程追踪分支
git remote prune origin --dry-run
# 清理无效的远程追踪分支
git remote prune origin
工作现场
规范:1.功能未开发完不要commit;2.没有commit之前不能切换分支
使用场景:在某个分支上开发某个功能,还没开发完毕但是要切换分支,切换前先保存现场
- 暂存工作现场
git stash
git stash save "自定义名词"
- 恢复工作现场
git stash list # 查看所有暂存的工作现场
git stash pop # 恢复+删除最顶端的工作现场
git stash apply # 恢复最顶端的工作现场
git stash drop # 删除最顶端的工作现场
git stash apply 【stash@{n}】 # 恢复指定工作现场
git stash drop 【stash@{n}】 # 删除指定工作现场
- 如果stash save后进行了commit,则stash pop的时候可能会有冲突
转移提交
- 假设开发时应该在dev分支上,但是选错了分支master,且已提交到master,可以用cherry-pick将已提交的commit转移分支
- 注意转移的时候一次只能复制一个提交,而且要按照提交的先后顺序复制
# 1.首先checkout到dev分支上
git checkout dev
# 2.将master分支上的错误提交复制到dev分支上,复制内容,生成新的commitid
git cherry-pick commitid # commitid查git log
# 3.切换到master分支
git checkout master
# 4.master分支切换到错误提交的上一个提交
git checkout commitid # commitid查git log
# 5.删除master分支
git branch -D master
# 6.在当前游离状态下新建一个master分支
git checkout -b master
变基
-
rebase当前分支到其他分支
-
一般不要在master上rebase
-
开发中要push代码到远程仓库之前,通常需要pull代码,参考博客
-
webstorm有两种选择:merge和rebase,推荐选择merge。(不同版本有差异)
-
idea上有三种选择:merge、rebase和branch default,推荐选择merge,stash。(不同版本有差异)
-
shelve是IDEA的功能,参考官方文档,StackOverflow 非常好用
-
3.标签
tag与branch的操作基本一致,因为tag就是一个仅可读的branch,标签对所有分支可见。
- 查看所有标签
git tag
git tag -l
git tag --list
- 给当前添加标签
git tag v1.0
git tag -a v1.0 -m "my version 1.0"
- 查看标签详情
git show v1.0
- 检出标签
git checkout v2.0.0
- 删除本地标签
git tag -d 标签名
- 删除远程标签
git push origin -d 标签名
- 推送到远端仓库
git push origin 标签名
git push origin --tags # 推送所有未提交的tag
- 更新到本地
git pull origin --tags
4.远程库
- 克隆远程库
git clone url
git clone git@github.com:xxx/xxx.git
- 查看远程库信息
git remote
git remote -v
- 添加远程库并为其指定别名
git remote add 远程库地址别名 远程库地址
git remote add origin git@github.com:xxx/xxx.git
- 修改远程库地址别名
git remote add 远程库地址别名 远程库地址新别名
git remote rename origin newname
- 删除对所有远程库分支的追踪
git remote remove 远程库地址别名
git remote rm 远程库地址别名
- 获取远程库
git fetch [远程库地址别名] [远程分支名]
git fetch origin master
- 切换到远程库
git checkout [远程库地址别名/远程分支名]
git checkout origin/master
- 合并远程库
git merge [远程库地址别名/远程分支名]
- 关联本地分支和远程分支(远程分支名和本地分支名同名时,写一个即可)
git push origin [远程分支名]:[本地分支名]
git pull origin [远程分支名]:[本地分支名]
- 拉取远程库,相当于先fetch再merge
git pull [远程库地址别名] [远程分支名]
git pull origin master
# 拉取远程库存在但本地不存在的分支
git pull origin dev
git checkout -b dev origin/dev
# or
git pull origin dev:dev # 第二个dev是本地仓库的分支名
- 把仓库推送到远程库
git push origin master
# 加了参数-u后,以后即可直接用git push 代替git push origin master
git push -u origin master
# 推送本地存在但远程库不存在的分支
git push -u origin dev
# or
git push --set-upstream origin dev
# 推送所有分支
git push origin --all
六、Git 工作流程
都是"功能驱动式开发"(Feature-driven development,FDD),具体参考博客
- GitHub Flow
- Create a branch
- Make changes
- Create a pull request
- Address review comments
- Merge your pull request
- Delete your branch
- GitLab Flow
协同开发
pull request 合并不同仓库代码,适合开源项目
merge request 合并同个仓库不同分支,适合团队内部项目
七、自建GitLab服务
- 在云服务器(CentOS 8 x64)上搭建GitLab,参考官方指引即可
- 要注意运行GitLab的内存一定要大,官方推荐至少4GB
- 为了短暂地体验一把,斥巨资买了一个服务器
- 氪金的力量非常强大,下载和安装的速度简直起飞,不一会就安装好了
- 域名需要配置才能用,这里直接访问服务器ip即可
- 首次访问需要输入密码
- 登录账号为root
- 逛一下平时看不到的管理中心
- 可以添加kubernetes集群
- 可以配置CI CD
- 好了接下来可以销毁服务器了💥
八、其他
.gitignore
.DS_Store
*.log
.vscode
.idea
node_modules
dist
temp
全局配置,在用户目录下创建 .gitignore
文件,修改 .gitconfig
[core]
excludesfile = .gitignore
.gitkeep
一个占位文件,存放于要让git追踪空文件夹,参考博客。可用于团队内部脚手架创建工程模板。