笔记:关于Git

需要了解的概念

  • 每个Git的配置文件都放在.git/config文件中
  • 工作区:电脑里的资源管理器中的目录
  • 暂存区:一个git本地库中暂时存储某些修改的地方,英文名stage或者index,一般存放在.git目录下的index文件
  • 版本库:也叫本地库,工作区的一个隐藏文件.git,不算工作区范围内
  • 远程仓库:一般使用github上创建的仓库,可把本地库中的内容传输给远程仓库
  • git一般工作流:
    • 把工作区的文件加入暂存区跟踪管理名单
    • 在工作区编辑文件
    • 添加修改到暂存区
    • 提交到版本库
    • 传输到远程仓库

也就是说文件一般是这么流的:工作区⇒暂存区⇒版本库⇒远程仓库

  • 分支:Git把版本库的每次提交串成一条时间线,这条时间线就是一个分支。分支(比如master)上有个指针(master指针),用来指向这个分支(或者说这条时间线)上的最新提交。而HEAD严格来说,指向的并不直接是时间线上的最新提交,而是这条时间线上“指向时间线上的最新提交”的master指针

每次提交,master分支就会变长一截,master指针的指向随着最新提交向前

  • 在主分支master基础上创建分支,比如dev
    • 会新建一个指针dev,指向的是master指针指向的那个最新提交
    • 还会把HEAD指针改为指向dev指针

所以Git创建分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化

通常master分支仅用来发布新版本,不直接在该分支工作。工作都在dev分支上,成员应该都有自己的分支,然后合并到dev。等dev中的新版本完成,再合并到master分支。而bug通常由一个临时分支来修复,用完就丢

  • 而创建分支dev后,新提交会导致由dev分支变长一截,dev指针跟着向前,而master指针是不动的

  • 假如我们在dev分支的工作完成了,就应该把dev分支的内容合并到master。合并有3种情况:“快进(无冲突)”、“非快进,修改不同文件(无冲突)”以及“非快进,修改相同文件(有冲突)”。不清楚看这个博客

    • 快进(无冲突):在master分支没有任何新提交的情况下,合并是直接把master指针指向dev指针指向的当前提交
    • 非快进,修改不同文件(无冲突):masterdev分支都有提交,且提交中修改的不是同一个文件,合并是直接取修改过的不同文件的并集
    • 非快进,修改相同文件(有冲突):masterdev分支都有提交,而提交中修改的是同一个文件,且有冲突,合并后会产生冲突,必须手动解决冲突后提交

不管哪种情况,Git合并都快,因为也就改改master指针和HEAD指针,dev指针是不改变的

若是在2个分支中都有新提交,那么就不属于快进

  • dev合并到master后甚至可以删除dev分支,也就是把dev指针删除掉,删掉后就剩1条master分支了

安装Git

Git官网安装Git,默认选项安装即可

安装Git的同时会安装Git Bash,这是一个便在windows下使用git命令的模拟终端

使用Git Bash跟踪文件前

  • 身份设置
$ git config --global user.name "<名字>"
$ git config --global user.email "<邮箱>"

其中的<名字>只是昵称

  • 创建本地库(或者称版本库)

    • 选择磁盘的任意一个地方,创建一个空目录。比如在D盘创建一个depot目录(若window系统,路径中最好不要有中文)

      • 直接在资源管理器创建
      • 或者在Git Bash中使用命令创建
      $ cd d:
      $ mkdir depot
      
    • 先切到这个创建的空目录,然后把这个创建的空目录变成Git可管理的本地库

    $ cd depot
    $ git init
    

    创建完可以发现在depot目录中有一个隐藏目录.git,这个目录是Git跟踪管理版本库的,千万别手动修改

正式使用Git Bash

  • 查看现在本地库的情况:未跟踪文件、已跟踪文件的修改记录、暂存库文件以及合并后产生冲突的文件
$ git status
  • 把文件加入Git的跟踪名单上

    • 先在资源管理器的本地库,即D:\depot中添加文件,即手动创建或者移动过来

    注意:要使用Git跟踪管理的文件一定要放在创建的本地库中,不然Git根本找不到这个文件

    注意:所有的版本控制系统都只能跟踪文本文件的改动,对于二进制文件是无法知晓其改动的。而MicrosoftWord是二进制格式,所以版本控制系统是没法跟踪Word文件的改动的

    • 把创建后未跟踪的文件添加进stage,即暂存区的跟踪管理名单
    $ git add <文件名>
    
  • 当工作区文件发生修改,将修改提交到暂存区

$ git add <文件名>

没错,也是使用git add命令

  • 提交暂存区的所有内容给版本库当前分支,一般是一个小功能完成后才提交到版本库的
$ git commit -m "<提交说明>"

commit时的<提交说明>最好有所规范,便于阅读

  • 查看工作区和版本库最新版本的区别
$ git diff -- <文件名>

记得--<文件名>之间有个空格

  • 撤回工作区的修改
$ git checkout -- <文件名>

记得--<文件名>之间有个空格

这种情况是“修改了工作区但是没有提交修改到暂存区”,即暂存区是干净的

该撤回可以让工作区回到修改前的状态,又叫做“丢弃工作区的修改”

  • 撤回git对文件的跟踪
$ git reset HEAD <文件名>
  • 撤回工作区的修改提交,即撤销add
$ git reset --hard HEAD

这种情况是“修改了工作区而且提交修改到了暂存区”,即暂存区不干净了(有一个你想撤回的错误git add

git reset命令是撤回了git add,即撤回了“提交修改到了暂存区”,那么暂存区又干净了。现在变成属于楼上那种情况了,即“修改了工作区但是没有提交修改到暂存区”

其实这种情况也可以归为楼下的范围的,只不过回退的版本是版本库的最新版本

  • 撤回commit记录,也就是版本回退(其实我觉得应该说成版本重置的)
$ git reset --hard <版本>

没错,也是它

同时会导致暂存区的提交记录丢失,楼上就用了这个特性

HEAD是指向“当前分支中指向最新提交的指针”的指针,如果想回退上一个版本就是HEAD^,上几个版本就加几个^,而上100个的话也写太多了,可以写成HEAD~100

<版本>可以用HEAD类的写法,或者是使用版本号(怎么查,楼下会说明)

版本回退会导致“想回退到的那个版本”之后的commit记录全部消失。若想撤销本次版本回退,那就是想回到“回退前的那个版本”,那不就是“版本重置”?没错,还是用git reset --hard <版本>,只不过这一次的<版本>需要填的是“已经不存在commit记录的版本的版本号”,这个使用楼下的git log命令已经找不到了,那怎么办?楼下的楼下会说明

  • 查看commit记录以及分支合并情况
$ git log

这个命令是查看commit记录的,会显示commit的那次版本的版本号、日期和提交说明

后加参数--pretty=oneline:变成简略版本的一行输出,包括版本号和提交说明

后加参数--graphcommit记录以图形形式展示,加上这个比较能看得出分支的合并情况

后加参数--abbrev-commit:版本号数据减少,剩前面几位数字

  • 查看历史命令,可查看“未来版本号”
$ git reflog

是记录在该版本库中使用过git commit命令以及git reset命令,因为其中会显示每次commitreset的版本号,所以可以用其来查询“由于回退而导致消失的commit记录的版本的版本号”,即所谓“未来的版本号”

  • 删除已跟踪的文件

    • 在资源管理器删除要删除的文件,或者用命令rm <文件名>,效果一致

    • 确定是否删除

      • 确定删除的话
      $ git rm <文件名>
      $ git commit -m "提交说明"
      

      注意:删除记录也要提交的,删除文件会导致工作区和版本库不一致

      • 如果只是在资源管理器把文件误删了,想撤回这个删除。那么就属于上文提到的“撤回工作区的修改”这种情况
      $ git checkout -- <文件名>
      

      如果是从未添加到版本库中的文件,误删了是没法通过Git恢复的

  • 创建分支

$ git branch <分支名>
$ git checkout -b <分支名>

第2个命令的含义是:创建并切换到分支

上文“撤回工作区修改”时也用过这个git checkout开头的命令,注意区分

由于git checkout开头的命令有相似性,所以在最新版的Git中,创建并切换到分支变成使用

$ git switch -c <分支名>
  • 切换分支
$ git checkout <分支名>

在最新版的Git中,切换分支变成使用

注意:在切换分支之前,要先确保状态“干净”(全部commit或者保存现场,如何保存?下下面有说明),否则会污染其它分支

$ git switch <分支名>
  • 查询分支
$ git branch

前面标*的就是当前分支

  • 合并指定分支到当前分支
$ git merge (--no-ff -m "<提交说明>") <分支名>

所以如果想合并到master分支,应该先切换到master分支,使用楼上的楼上的切换命令

如果是“快进”情况的话,合并完就发现master的最新提交跟dev分支的最新提交完全一致了,因为仅仅是将master指针指向dev指针指向的那次提交;如果不是“快进”情况,那么合并后会产生冲突,如何解决?请看楼下的楼下

参数--no-ff -m "<提交说明>"":禁用“快进”合并模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息

  • 删除分支
$ git branch -d <分支名>

因为创建、合并和删除分支非常快,所以Git鼓励使用分支完成某个任务,合并后再删掉分支.这和直接在master分支上工作效果是一样的,但过程更安全

参数-d换成-D:如果有一个临时分支,它commit了,现在应该合并到主分支,但你不想合并了,想直接删除这个临时分支,使用参数-d是删除不了的,会弹出error,但可以使用-D参数强行删除

  • 解决冲突
    使用git status命令查看产生冲突的文件,打开文件看看内容。比如下面这个就是有冲突的位置,在master分支中这个位置的句子是“我是master”,而在dev分支中这个位置的句子却是“我是dev”。把内容改成想要的(记得删掉冲突标识<<<<>>>>====),然后再git add以及git commit
<<<<<<< HEAD
我是master
=======
我是dev
>>>>>>> dev

提交后可用git log --graph看一下分支的合并情况,上文有说明过这个命令

如果是想合并到master分支的,那么冲突就产生在master分支,被合并的分支是不会被影响的。所以解决冲突只需要在“想合并到”的分支上

  • 保存工作区现场
$ git stash

bug通常会创建一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。但如果现在在dev分支,有个master分支上的bug需要立即修复,而在dev中的工作区的工作还没完成(即:工作区有修改然后没add的情况),直接切会污染分支,可以先把工作现场保存起来,等之后想恢复再恢复

注意:不保存暂存区的内容

后加参数list:是查看保存记录

后加参数apply <stash记录的id>:恢复保存的现场,但记录还存在

后加参数pop:恢复保存的现场,同时删除记录

后加参数drop <stash记录的id>:删除保存的记录,即apply + drop = pop

上面说到可以利用新开分支来处理bug,若是处理完在master分支上的bug,但dev其实早期是从master中切出来的,那这个bug其实在dev分支上也存在,若是不想在dev上重复一次修复同一个bug的操作,还有简单的方法,看楼下

  • 复制特定提交
$ git cherry-pick <commit id>

只会复制该次提交的修改部分,而不是一整个提交

可用于在不同分支修复同一个bug

  • 连接远程仓库(推荐ssh

    • 查看是否已经创建ssh key
      在用户主目录(如C:\Users\DELL)下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsaid_rsa.pub这两个文件,如果已经有了,说明已经创建好了

    id_rsa:私钥,不可泄漏;id_rsa.pub:公钥,告诉别人没关系

    • 创建ssh key
    $ git ssh-keygen -t rsa -C "<邮箱>"
    

    这里一路回车就ok。由于这个key也不是用于军事目的,所以也无需设置密码

    • github创建ssh链接:
      登录githubsetting里的ssh keys里面添加一个ssh keytitle任意,key文本框输入id_rsa.pub文件的内容

    • github上创建一个远程仓库

    github的免费仓库是所有人可见的,不要放敏感信息。如果想要有私人仓库:花钱可以得到github私有仓库,或者自己搭建git服务器

    • 连接本地库和远程仓库
    $ git remote add <名字> <ssh链接>
    

    <名字>:git(没改变github里的)中该远程仓库的名字,取什么名字都可以。通常是使用originGit默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库。或者使用项目的简略名字

    <ssh链接>:这个在github中远程仓库里可以复制,比如git@github.com:7AAAAAAA/test.git

  • 查看已连接的远程仓库

$ git remote (-v)

不加-v显示的只有git中该远程仓库的名字;加上参数-v会显示push的地址,但如果没有推送权限,就看不到push的地址

  • 推送指定分支到远程仓库
$ git push (-u) <远程库在git的名字> <本地库指定分支名>

<远程库在git的名字>指的是之前在“连接本地库和远程仓库”时自定义的名字,若忘记了,可使用git remote查询

参数-u的作用是:把本地库指定分支与远程仓库的同名分支关联了,下次推送就不用使用这个-u参数了

通常只有master(主分支)和dev(开发分支)需要与远程仓库同步,feature(功能分支)的推送取决是否需要在此分支上进行开发,而bug分支一般不推送,除非有要求

不同的人向远程仓库的某一分支都推送了自己本地的修改版本,可能产生冲突,解决方法是:每次推送前先把远程仓库中的最新版本拉取到本地,然后合并,解决冲突之后再推送。如何拉取远程仓库的最新版本到本地?看楼下

  • 从远程仓库拉取
$ git pull

若是拉取失败,可能是没有指定本地分支和远程仓库分支的链接(会提示no tracking information),那么可以通过以下命令设置它们的链接

$ git branch --set-upstream-to=<远程仓库>/<远程仓库的分支> <本地分支名>
  • 从远程仓库克隆一个本地库
$ git clone <ssh链接>

第1次使用pushclone时会弹出ssh警告,这是因为Git使用ssh连接,而ssh连接在第一次验证GitHub服务器的Key时,需要你确认GitHubKey的信息是否真的来自GitHub的服务器,输入yes回车即可。Git会输出一个警告,告诉你已经把GitHubKey添加到本机的一个信任列表里了

默认情况下,克隆的本地库默认会与“克隆来源”的远程仓库连接,且连接的名字为origin(即可通过git remote命令查看到origin),并且只能看到master分支,可以使用以下命令创建本地库分支和与远程仓库分支关联

$ git checkout -b <分支名> <远程仓库>/<远程仓库的分支>

通过克隆的本地库中<远程仓库>默认为origin,本地库分支和远程仓库分支的名称最好一致

  • 把本地未push的分叉提交历史整理成直线,也称“变基”(啊,看不太懂这个,有空看看这个
$ git rebase

提交不是一条线(提交历史不清晰)(原因可能是pull后自动merge了别人的提交,产生了分叉,最后自动产生了一个merge后的新的提交版本)。解决办法:push前使用rebase(过程就是:先把刚才自己提交版本临时保存,再把版本更新到最新远程,再把临时保存的提交重新提交)

特点是把分叉的提交历史“整理”成一条直线,看上去更直观。缺点是本地的分叉提交已经被修改过了
该命令可以使得查看历史提交的变化变得更加容易,因为分叉提交需要三方对比

  • 创建标签
$ git tag <标签名> <commit id>

标签tag就是一个让人容易记住的有意义的名字,它其实是指向某个commit的指针,但特殊的是,这个指针是不移动的

注意:标签总是和某个commit挂钩。如果这个commit既出现在master分支,又出现在dev分支,那么在这2个分支上都可以看到这个标签

注意:创建的标签都存储在本地,不会自动推送到远程,可也可以手动推送,看下下面

打标要先切换到需要打标签的分支上

如果不加参数<commit id,那就是默认情况,默认标签是打在最新提交的commit上的;否则就是为指定commit打标签。可使用git log命令查看commit id

还有自带说明的标签,然而其实就是加参数

$ git tag -a <标签名> -m "" <commit id>
  • 查看标签
$ git tag

标签不是按时间顺序列出,而是按字母排序的

  • 查看标签信息
$ git show <标签名>

会显示标签的对应commit信息以及标签说明等

  • 删除本地标签
$ git tag -d <标签名>
  • 推送标签到远程库
$ git push <远程仓库在git中的名字> <标签名>

若<标签名>为--tags,可以一次性推送全部未推送的标签到远程仓库

  • 删除已推送到远程库的标签

先在本地删除要删除的标签,使用楼上的楼上的命令
然后再从远程删除,命令如下

$ git push <远程仓库在git中的名字> :refs/tags/<标签名>
  • 自定义Git Bash
$ git config --global color.ui tru

Git会适当地显示不同的颜色

  • 忽略文件
    Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。可以使用*!符号,比如*.class表示忽略所有.class文件,!test.txt表示不忽略test.txt文件

注意:.gitignore文件本身要放到版本库里,并且可以对.gitignore文件做版本管理

如果想添加一个文件到Git,但发现添加不了,原因肯是这个文件被.gitignore忽略了,可以选择强制添加,使用-f,如下

$ git add -f <文件名>
  • 查看忽略文件
$ git check-ignore -v <文件名>
  • 为命令配置别名
$ git config --global alias.<别名> <原本的命令>

比如$ git config --global alias.st status命令就是告诉Git,以后st就表示status

加参数--global表示全局,也就是该命令在电脑的所有Git仓库下都有用,如果不加,即表示只对当前仓库起作用

posted @ 2021-03-06 16:42  有机物与鱼  阅读(44)  评论(1编辑  收藏  举报