一、基本使用
1. 基本理论
Git 是一个免费的、开源的分布式版本控制系统,可以快速高效地处理从小型到大型的项目;版本控制是一种记录一个或者若干个文件内容变化,以便来查阅特定版本修订情况的系统
集中化版本控制系统:SVN, CVS,以及Perforce等分布式版本控制系统:Git
Git 与 SVN 的区别
SVN 是集中化版本控制系统,版本库是集中放在中央服务器上的,而工作的时候,用的都是自己的电脑,所以首先要从中央服务器得到最新的版本,然后工作,完成工作后,需要把自己的做完的活推送到中央服务器;集中式版本控制系统必须联网才能工作,对网络带宽要求比较高
Git 是分布式版本控制系统,没有中央服务器,每个人的电脑是一个完整的版本库,工作的时候就不需要联网了,因为版本都在自己的电脑上;协同的方法是这样的:比如说自己在电脑上修改了A文件,其他人也在电脑上修改了文件A,这时你们两之间只需要把各自的修改推送给对方,就可以互相看到对方的修改了
Git 是目前世界上最先进的分布式版本控制工具
查看不同级别的配置文件
#查看系统config
git config --system --list
#查看当前用户(global)配置,这个文件在用户家目录下,是一个隐藏文件 .gitconfig
git config --global --list
github 设置免密登录
#1.生成公私钥(在家目录下的.ssh文件夹下)
ssh-keygen -t rsa -C 1245829528@qq.com
#2.将公钥拷贝到github上的 SSH keys 下面
#3.测试是否配置成功,只要没有显示 Permission denied 就表示配置成功了
ssh -T git@github.com
#4.之后就可以进行push这些操作了
2. 本地仓库搭建
仓库初始化
创建本地仓库的方法有两种:1.创建一个全新的仓库 2.克隆全程仓库
git init #在当前目录新建一个 git 代码库
#方法一:
git remote rm origin #删除之前原始的目的远程仓库
git remote add origin [url] #设置新的远程目的仓库
#方法二:把URL替换成新的URL地址
git remote set-url origin [url]
#方法三:直接修改 .git/config 配置文件
# 克隆一个项目到本地
git clone [url]
执行后就可以看到目录项多出一个 .git目录,关于版本的所有信息都在这个目录里
gitignore 文件
忽略文件:有时我们不想把某些文件纳入版本控制中,比如数据库文件,临时文件,设计文件等
在主目录下新建 .gitignore 文件,此文件有如下规则:
1.忽略文件中的空行或者以 ‘#’ 开始的行
2.可以使用 Linux 通配符
3.如果名称的最前面有一个感叹号 ‘!’,表示例外规则,将不被忽略
4.如果名称最前面是一个路径分隔符 ‘/’,表示要忽略的文件在此目录下,而子目录中的文件不忽略
#为注释
*.txt #忽略所有的 .txt 结尾的文件,这样上传的话不会被选中
!lib.txt #但lib.txt除外
/tmp #忽略根目录下的tmp中的所有文件
build/ #忽略当前录下的 build 目录下的所有文件,写相对路径就行,不需要写绝对路径,不要写成 ./build/
常用 git 命令
Git branch
查看分支
git branch #显示本地分支
git branch -r #显示远程分支
git branch -a #查看本地和远程分支
git branch -m {} #修改分支名
git branch -d [branchname] #删除本地分支
#删除远程分支(当前分支无法删除,删除当前分支会报错)
git push --delete origin [branch](注意这个远程分支不要加上origin/)
远程分支相关的操作都是 git push --...开头
本地分支关联远程分支 : 在本地创建好一个分支后,就可以设定其关联到一个同名的远程分支上,这样的话直接 git push 后面不需要接参数 就会默认提交到和你本地分支名同名的远程分支上去
所以建议本地分支和远程分支起相同的名字
当 git push 的时候如果出现 git push --set-upstream origin
,这就是要指定本地分支对应的远程分支
解决方法:git branch --set-upstream-to=origin/remote_branch your_branch
其中 origin/remote_branch
是你本地分支对应的远程分支;your_branch
是你当前的本地分支
git branch -m oldName newName #1.本地分支重命名
git branch -m oldName newName #2.远程分支重命名就是删除对应的远程分支然后再新建一个
git push origin --delete oldName #3.删除远程分支
git push origin newName #4.上传新命名的本地分支
git branch --set-upstream origin/newName #5.把修改后的的本地分支和远程分支关联
注: --set-upstream 等价于 -u ,这两个都一个意思
git push {local_name}:{remote_name} #将本地分支代码提交到指定远程分支
查看本地本地分支关联的远程分支,三种方式
git branch -vv
git remote show origin
cat .git/config
Git pull
git pull origin main #拉取远程分支到本地
# 1.如果拉取过程中显示 refusing to merge unrelated histories
# 原因是两个分支是两个不同的版本,具有不同的提交历史,提交时加上加一句 --allow-unrelated-histories 即可
#2.如果拉取过程中显示conflict, 多半原因是本地文件和远程文件有冲突,解决完冲突直接push即可
Git clone
git clone -b [branchName] [ssh地址]
来获取指定的分支,注意不同的分支拉取下来后的文件夹的名字都是相同的;比如 仓库地址为 github.com/Chao.git,存在两个分支 master 和 draft
git clone -b draft ``github.com/Chao.git
git clone ``github.com/Chao.git
不加任何参数默认 clone 的是 master 分支
这两条命令clone下来的文件夹都叫 Chao
Git 只克隆指定仓库中的指定文件夹
Git1.7.0 以后加入了 Sparse Checkout 模式,这使得 Check Out 指定文件或者文件夹成为可能
- 初始化一个本地仓库:git init <要新建的本地目录名称>
- 指定远程仓库:git remote add -f origin <远程仓库>
- 指定克隆模式–稀疏克隆:git config core.sparsecheckout true
- 指定克隆的文件夹(或者文件):echo “要克隆的文件名” >> .git/info/sparse-checkout
- 拉取远程文件:git pull origin master
一个完整的例子:
git init mypathname
cd mypathname
git remote add -f origin https://github.com/bestswifter/MySampleCode.git
git config core.sparsecheckout true
echo "CornerRadius" >> .git/info/sparse-checkout
git pull origin master
Git tag
git tag 用于某一时间点的版本做标记,常用于版本发布
git tag -a v0.0.1 -m "v0.0.1发布"
git push origin v0.0.1
#删除tag
git tag -d v0.0.1 #删除本地tag
git push origin :refs/tags/v0.0.1 #删除远程tag
Git Commit
注意:只要是代码被纳入了本地库–也就是只要是指行了git commit 操作,都可以找回
git log 用来查看 git commit 的完整提交历史信息;git reflog 来查看简略信息
git log 只能查看到 git commit 的信息
git reflog 是一种记录HEAD和分支引用的运动机制,有了 git reflog,做什么操作都不怕,只要是 commit 过的修改,都是可以恢复的;用 git reflog 可以查看所有 git 相关操作的记录,并有对应的hash值,有了hash值,想恢复到哪里都可以;
git commit -a
参数作用:如果你只是修改或者删除了文件,加上 -a 参数,可以帮你省一步 git add 命令;但是如果是新增文件,还是得使用 git add,否则会提示 Untracked
type
用来说明 commit 的类别,只允许使用下面7个标识:
- feat:新功能(feature)
- fix: 修补bug
- refactor:重构(即不是新增功能,也不是修改bug的代码变动)
- test:增加测试
- docs:文档更新(documentation)
- style:不影响代码逻辑的代码修改(修改空白字符,格式缩进,补全缺失的分号等,没有改变代码逻辑)
- chore:不属于上述流程的其他类别,如构建过程或辅助工具的变动
比如: git commit -m “refactor: ListHCI”
二、报错处理
弹出这个信息说明系统保存的密码和用户名是正确的,可以连接上;之所有有错误是因为一开始的设置,git clone 时使用的是 https 的网址,应该复制 ssh 的网址;打开项目下的隐藏文件 ./.git,修改 config 文件,将 origin 的https改为ssh网址即可,
替换url为SSH方式
url = git@github.com:beyondverage0908/MyMD.git
最后保存退出即可
附:查看Github的ssh是否正常连接,使用
ssh -T git@github.com
先删除缓存(将所有文件变成 untracked 状态)之后再进行 git 的提交
git rm -r --cached . # 清除本地缓存
git add . # 将文件提交至暂存区
git commit -m 清除git缓存, 解决gitignore问题
git push # 提交到远程仓库
当我们在 github 版本库中发现一个问题后,你在 github 上对它进行了在线的修改;或者你直接在 github 上的某个库中添加 readme 文件或者其他什么文件,但是没有对本地库进行同步。这个时候当你再次有 commit 想要从本地库提交到远程的 github 库中时就会出现push失败的问题。
解决方法:这个问题是因为远程库与本地库不一致造成的,那么我们把远程库同步到本地库就可以了
#重新拉取一下远程仓库
git pull origin main
附:git pull的作用是从一个仓库或者一个本地的分支拉取并且整合代码
git pull [<options>] [<repository> [<refspec>]]
git pull 相当于 git fetch 跟着一个 git merge FETCH_HEAD; 是仓库的名字, 是分支的名字,如果都不写则会有一个默认值
使用方式
git pull # 按照git branch 设置的默认跟踪的服务器的分支来拉取
git pull origin main # 拉取远程服务器 origin 的 main 分支
下午在Git提交的时候发现一个很大问题,我这个提交的文件有的大,超过了100M,git 显示报错超出文件大小限制,我以为把这个文件删除掉就能解决,发现删除了本地文件也没啥用处
网上找了好多方法,发现用下面这些方法
git rm -r --cached . #不起作用
git rm <file_name> #显示找不到文件,(确实,我这个本地文件已经被删除掉了)
思来想去想了好久
出现这种情况是因为提交到了本地仓库但是没有提交到远程仓库,如果一直不先把本地仓库的这个大文件消除,则直接 push 会不断报错,因为这个大文件始终在这个本地仓库中
解决方案:使用版本回退
# soft回退到上一个版本,只回退commit的信息,如果还要提交则直接 commit 即可
git reset --soft HEAD^
# hard彻底回退到某个版本,本地的源码也会变为上一个版本的内容
git reset --hard HEAD^
提示这种情况只要MR没有冲突就照样可以合入,参考 StackFlow 这篇文章解答
三、技术细节
git merge 和 git rebase 的区别
答:两者都是用来合并分支,准确来说 merge 是用来合并分支,而 rebase 是用来衍合分支
merge 操作会生成一个新的节点,与之前的提交分开显示;而 rebase 操作不会生成新的节点,是将两个分支融合到一个线性的提交
如果想要一个干净的,没有 merge commit 的线性历史树,就选择 rebase
如果想要保留完整的历史记录,并且想要避免重写 commit history 的风险,就选择 merge
比如以下两个分支:
D--E test
/
A--B--C--F master
#使用 git merge test 命令
D----E
/ \
A--D--C--F--G -- test,master
#使用 git rebase test 命令
A--B--D--E--C--F' #将两个分支融合成一个线性的提交
使用场景:
rebase 的缺点就是会丢失部分提交的原始信息,所以公共分支一般不用rebase,都使用merge
但是自己的需求分支可以用rebase,这样自己看着简洁
git squash 的用法
在开发一个功能的时候会反复的提交代码,会造成一个功能有很多次提交,在我们要向master做分支合并的时候,就会出现很多commits,在合并以后同一个功能的commits就会很多,导致我们无法清晰的知道这个功能关联的commit有哪些,这个squash就是优化我们的commits信息,让我们的版本仓库看起来简洁明了,功能点一目了然
gitlab上的 Squash commit 就会自动将我们所有的commit压缩成一个commit,参考 git squash 用法
这样可以在master主分支节点上只看到一个提交,不管你在自己的feature分支上有多少次提交
git revert 和 git reset 的区别
答:git revert {commit} 和 git reset {commit} 都是回退到指定的commit
git revert 是用一次新的提交来回滚到之前的commit,也就是用一个新提交来消除之前的一个历史提交所作的任何修改;而 git reset 是 用于移动分支指针的位置到指定的 commit
总结: git revert 是让 HEAD 继续前进,而 git reset 是把 HEAD 向后移动了一下
应用场景:用 git 提交代码的时候,如果发现这一次的 commit 是错误的,那么就有以上两种处理方法,推荐使用 git reset
git reset --hard commit_id
其中 commit_id 可通过 git log
命令查看
git reset 常犯的错误:
- 本地 git reset 之后回退到某个版本
- 回退版本后,未push到远程就修改了本地代码
- 修改完后再push到远程,提示先pull(问题就是:pull远程代码时就会覆盖本地修改的了)
操作分析:
一开始是这样:
A - B - C - D 远程 A - B - C - D 本地
git reset --hard B 之后:
A - B - C - D 远程 A - B 本地
第二步,修改本地代码,记为 E:
A - B - C - D 远程 A - B - E 本地
1.如果我们现在 pull 远程代码,情况如下:
A - B - C - D 远程 A - B - E - D’ 本地,这其中 D’ 就包含了C和D的改动,因为 git pull 相当于 git fetch 加上 git merge。这个时候 merge 的是 “Fetched HEAD”,也就是远程的 D。同时,D’ 的 message 应该会出现一句 “merge … from …”
2.如果希望远程是 A - B - E,那就不要 pull,直接 git push --force 强制推送
总结:
- 当 git commit 之后想要撤销这次commit,直接使用
git reset --soft HEAD^
;如果只是想修改commit 的信息,直接 git commit --amend - 如果是pull的时候,拉错了分支,想要回退到之前没有pull时的状态,使用
git reset --hard HEAD^
git fetch 的用法
首先需要明白,当使用 git commit 命令的时候,会产生一个 commit-id,这是一个唯一能标识一个版本的序列号,可通过 git reflog 命令查看每次 commit 产生的这个序列号。在使用 git push 之后,这个序列号还会同步到远程 repo
而 FETCH_HEAD 是一个版本链接,记录在本地的 .git/FETCH_HEAD 文件中,指向最近一次从远程仓库拉取下来的分支的末端版本,其就是上面的序列号
git fetch origin branch-name
就是更新这个 FETCH_HEAD ,获取远程仓库对应分支的最新的 commit-id 到 .git/FETCH_HEAD 文件中
git merge FETCH_HEAD
或者 git merge origin branch-name
将目标分支最新的 commit 记录合并到当前分支;附:git merge 会比较目标分支的commit-id和当前分支的commit-id是否相同,如果不相同则拉取目标分支,否则不拉取
注意:git merge 只能往主分支合并,所以 merge 时所在本地分支要在 master 分支,再去 merge 其他的分支;
而这两个命令就等价于:git pull origin branch-name
总结:git fetch 和 git merge 的原理需要弄清楚,但是以后使用推荐直接使用 git pull
git stash的用法
git stash 是用于将修改的内容保存至堆栈区,能够将所有未提交的修改(工作区和暂存区)保存至堆栈中,用于后续恢复当前工作目录
git stash pop 弹出之前保存的内容
应用场景:比如自己已经开发完成了一个功能,并且成功merge到了主分支上;然后就可以接着开发第二个功能了,开发到了一半发现之前第一个功能出bug了,现在需要紧急修复,但是第二个功能又没有开发完,所以不能把现有的代码直接push上去,这时就有两种解决方案
- 出现bug的是一个独立的文件,也就是这个文件没有与第二个功能开发相关的部分,则可以使用只单独修改这个文件,然后单独push这个文件
git add file/fix.go
git commit -m " "
git push
- 如果出现bug的不是一个单独的文件,自己已经在这个文件上添加了其他的功能代码,则我们需要先把这些改动的代码暂时保存住,再修改bug
1.git stash 这个会把距离上一次commit的代码之后做的修改都保存起来,
让本地代码恢复到上一次commit时的代码
2.然后修改 fix bug
git add .
git commit -m ""
git push
3.修改完成再恢复之前保存的代码 git stash pop