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
相关推荐
k09339 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
神奇夜光杯17 分钟前
Python酷库之旅-第三方库Pandas(202)
开发语言·人工智能·python·excel·pandas·标准库及第三方库·学习与成长
Themberfue19 分钟前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
plmm烟酒僧21 分钟前
Windows下QT调用MinGW编译的OpenCV
开发语言·windows·qt·opencv
测试界的酸菜鱼32 分钟前
Python 大数据展示屏实例
大数据·开发语言·python
Mephisto.java36 分钟前
【大数据学习 | kafka高级部分】kafka中的选举机制
大数据·学习·kafka
晨曦_子画42 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
Black_Friend1 小时前
关于在VS中使用Qt不同版本报错的问题
开发语言·qt
假装我不帅1 小时前
asp.net framework从webform开始创建mvc项目
后端·asp.net·mvc