Git 学习

一、基本使用

1. 基本理论

Git 是一个免费的、开源的分布式版本控制系统,可以快速高效地处理从小型到大型的项目;版本控制是一种记录一个或者若干个文件内容变化,以便来查阅特定版本修订情况的系统

集中化版本控制系统:SVN, CVS,以及Perforce等分布式版本控制系统:Git

Git 与 SVN 的区别

SVN 是集中化版本控制系统,版本库是集中放在中央服务器上的,而工作的时候,用的都是自己的电脑,所以首先要从中央服务器得到最新的版本,然后工作,完成工作后,需要把自己的做完的活推送到中央服务器;集中式版本控制系统必须联网才能工作,对网络带宽要求比较高

Git 是分布式版本控制系统,没有中央服务器,每个人的电脑是一个完整的版本库,工作的时候就不需要联网了,因为版本都在自己的电脑上;协同的方法是这样的:比如说自己在电脑上修改了A文件,其他人也在电脑上修改了文件A,这时你们两之间只需要把各自的修改推送给对方,就可以互相看到对方的修改了

Git 是目前世界上最先进的分布式版本控制工具

查看不同级别的配置文件

bash 复制代码
#查看系统config
git config --system --list

#查看当前用户(global)配置,这个文件在用户家目录下,是一个隐藏文件 .gitconfig
git config --global --list

github 设置免密登录

bash 复制代码
#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.克隆全程仓库

bash 复制代码
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.如果名称最前面是一个路径分隔符 '/',表示要忽略的文件在此目录下,而子目录中的文件不忽略

bash 复制代码
#为注释
*.txt     #忽略所有的 .txt 结尾的文件,这样上传的话不会被选中
!lib.txt  #但lib.txt除外
/tmp      #忽略根目录下的tmp中的所有文件
build/    #忽略当前录下的 build 目录下的所有文件,写相对路径就行,不需要写绝对路径,不要写成 ./build/

常用 git 命令

Git branch

查看分支

bash 复制代码
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 是你当前的本地分支

bash 复制代码
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} #将本地分支代码提交到指定远程分支

查看本地本地分支关联的远程分支,三种方式

bash 复制代码
git branch -vv
git remote show origin
cat .git/config
Git pull
bash 复制代码
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 指定文件或者文件夹成为可能

  1. 初始化一个本地仓库:git init <要新建的本地目录名称>
  2. 指定远程仓库:git remote add -f origin <远程仓库>
  3. 指定克隆模式--稀疏克隆:git config core.sparsecheckout true
  4. 指定克隆的文件夹(或者文件):echo "要克隆的文件名" >> .git/info/sparse-checkout
  5. 拉取远程文件:git pull origin master

一个完整的例子:

bash 复制代码
  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 用于某一时间点的版本做标记,常用于版本发布

bash 复制代码
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"

二、报错处理

报错 You've successfully authenticated, but GitHub does not provide shell access

弹出这个信息说明系统保存的密码和用户名是正确的,可以连接上;之所有有错误是因为一开始的设置,git clone 时使用的是 https 的网址,应该复制 ssh 的网址;打开项目下的隐藏文件 ./.git,修改 config 文件,将 origin 的https改为ssh网址即可,

替换url为SSH方式

bash 复制代码
url = git@github.com:beyondverage0908/MyMD.git

最后保存退出即可

附:查看Github的ssh是否正常连接,使用

bash 复制代码
ssh -T git@github.com

目录下的 .gitignore 文件不生效的问题

先删除缓存(将所有文件变成 untracked 状态)之后再进行 git 的提交

bash 复制代码
git rm -r --cached .        # 清除本地缓存
git add .                   # 将文件提交至暂存区
git commit -m  清除git缓存, 解决gitignore问题 
git push                    # 提交到远程仓库

error:failed to push some refs to

当我们在 github 版本库中发现一个问题后,你在 github 上对它进行了在线的修改;或者你直接在 github 上的某个库中添加 readme 文件或者其他什么文件,但是没有对本地库进行同步。这个时候当你再次有 commit 想要从本地库提交到远程的 github 库中时就会出现push失败的问题。

解决方法:这个问题是因为远程库与本地库不一致造成的,那么我们把远程库同步到本地库就可以了

bash 复制代码
#重新拉取一下远程仓库
git pull origin main                

附:git pull的作用是从一个仓库或者一个本地的分支拉取并且整合代码

bash 复制代码
git pull [<options>] [<repository> [<refspec>]]

git pull 相当于 git fetch 跟着一个 git merge FETCH_HEAD; 是仓库的名字, 是分支的名字,如果都不写则会有一个默认值

使用方式

bash 复制代码
git pull              # 按照git branch 设置的默认跟踪的服务器的分支来拉取
git pull origin main  # 拉取远程服务器 origin 的 main 分支

文件过大删除也不生效

下午在Git提交的时候发现一个很大问题,我这个提交的文件有的大,超过了100M,git 显示报错超出文件大小限制,我以为把这个文件删除掉就能解决,发现删除了本地文件也没啥用处

网上找了好多方法,发现用下面这些方法

bash 复制代码
git rm -r --cached . #不起作用
git rm <file_name>   #显示找不到文件,(确实,我这个本地文件已经被删除掉了)

思来想去想了好久

出现这种情况是因为提交到了本地仓库但是没有提交到远程仓库,如果一直不先把本地仓库的这个大文件消除,则直接 push 会不断报错,因为这个大文件始终在这个本地仓库中

解决方案:使用版本回退

bash 复制代码
# soft回退到上一个版本,只回退commit的信息,如果还要提交则直接 commit 即可
git reset --soft HEAD^  

# hard彻底回退到某个版本,本地的源码也会变为上一个版本的内容
git reset --hard HEAD^  

Git Merge 时提示 "The source branch is 2 commits behind the target branch"

提示这种情况只要MR没有冲突就照样可以合入,参考 StackFlow 这篇文章解答

三、技术细节

git merge 和 git rebase 的区别

答:两者都是用来合并分支,准确来说 merge 是用来合并分支,而 rebase 是用来衍合分支

merge 操作会生成一个新的节点,与之前的提交分开显示;而 rebase 操作不会生成新的节点,是将两个分支融合到一个线性的提交

如果想要一个干净的,没有 merge commit 的线性历史树,就选择 rebase

如果想要保留完整的历史记录,并且想要避免重写 commit history 的风险,就选择 merge

比如以下两个分支:

bash 复制代码
         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 常犯的错误:

  1. 本地 git reset 之后回退到某个版本
  2. 回退版本后,未push到远程就修改了本地代码
  3. 修改完后再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 强制推送

总结:

  1. 当 git commit 之后想要撤销这次commit,直接使用 git reset --soft HEAD^;如果只是想修改commit 的信息,直接 git commit --amend
  2. 如果是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上去,这时就有两种解决方案

  1. 出现bug的是一个独立的文件,也就是这个文件没有与第二个功能开发相关的部分,则可以使用只单独修改这个文件,然后单独push这个文件
bash 复制代码
git add file/fix.go
git commit -m " "
git push
  1. 如果出现bug的不是一个单独的文件,自己已经在这个文件上添加了其他的功能代码,则我们需要先把这些改动的代码暂时保存住,再修改bug
bash 复制代码
1.git stash 这个会把距离上一次commit的代码之后做的修改都保存起来,
            让本地代码恢复到上一次commit时的代码
2.然后修改 fix bug
git add .
git commit -m ""
git push
3.修改完成再恢复之前保存的代码 git stash pop
相关推荐
图像处理大大大大大牛啊6 分钟前
使用mingw64 编译 QT开发流程
开发语言·c++·qt·命令模式
愿尽11 分钟前
JavaWeb【day14】--(SpingBoot原理)
java·spring boot·后端
计算机学姐25 分钟前
基于python+django+vue的农业管理系统
开发语言·vue.js·后端·python·django·pip·web3.py
洪大宇27 分钟前
Windows Python 指令补全方法
开发语言·python
一尘之中39 分钟前
链路聚合(Link Aggregation)
网络·人工智能·学习
惜.己41 分钟前
基于Spring搭建SpringMvc框架
java·开发语言·后端·spring·tomcat·mvc·idea
箬敏伊儿1 小时前
springboot项目中 前端浏览器访问时遇到跨域请求问题CORS怎么解决?has been blocked by CORS policy
java·前端·spring boot·后端·spring
爱技术的小伙子1 小时前
【30天玩转python】面向对象编程基础
开发语言·python
theoxiong1 小时前
Python的Scapy库详解
开发语言·python·网络协议·tcp/ip·http·信息与通信·scapy
_晓夏_1 小时前
【JVM 工具命令】JAVA程序线上问题诊断,JVM工具命令的使用,jstat, jstack,jmap命令的使用
java·开发语言·jvm·jvm命令工具·java程序线上问题定位命令·java程序问题故障排查命令·jvm线上问题故障排查