Git 安装
Git是开放源代码的代码托管⼯具,最早是在Linux下开发的。开始也只能应⽤于Linux平台,后⾯慢慢的被移植到windows下,现在,Git可以在Linux、Unix、Mac和Windows这⼏⼤平台上正常运⾏了。
Linux-centos
安装git
sudo yum -y install git
安装好之后就可以,查看git版本
git --version
Linux-ubuntu
安装git
sudo apt-get install git -y
安装好之后就可以,查看git版本
git --version
Git 基本操作
创建 Git 本地仓库
要提前说的是,仓库是进⾏版本控制的⼀个⽂件⽬录。我们要想对⽂件进⾏版本控制,就必须先创建⼀个仓库出来。
创建⼀个Git 本地仓库对应的命令为 git init ,注意命令要在⽂件⽬录下执⾏,例如:
当前⽬录下多了⼀个 .git 的隐藏⽂件,.git ⽬录是Git来跟踪管理仓库的,不要⼿动
修改这个⽬录⾥⾯的⽂件,不然改乱了,就把Git仓库给破坏了。
我们可以对.git进行查看:
配置Git
当安装Git后⾸先要做的事情是设置你的用户名称和e-mail地址,这是⾮常重要的。配置命令为:
git config [--global] user.name "Your Name"
git config [--global] user.email "email@example.com"
# 把 Your Name 改成你的昵称
# 把 email@example.com 改成邮箱的格式,只要格式正确即可
其中[--global]是一个可选项。如果使⽤了该选项,表⽰这台机器上所有的Git仓库都会使⽤这个配置。如果你希望在不同仓库中使⽤不同的 name 或 e-mail ,可以不要 --global 选项,但要注意的是,执⾏命令时必须要在仓库⾥。
查看配置命令:
git config -l
删除对应的配置命令:
git config [--global] --unset user.name
git config [--global] --unset user.email
认识工作区、暂存区、版本库
- ⼯作区:是在电脑上你要写代码或⽂件的⽬录。
- 暂存区:英⽂叫stage或index。⼀般存放在.git⽬录下的index⽂件(.git/index)中,我们把暂存区有时也叫作索引(index)。
- 版本库:⼜名仓库,英⽂名repository。⼯作区有⼀个隐藏⽬录.git,它不算⼯作区,⽽是Git的版本库。这个版本库⾥⾯的所有⽂件都可以被Git管理起来,每个⽂件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
- 图中左侧为⼯作区,右侧为版本库。Git的版本库⾥存了很多东西,其中最重要的就是暂存区。
- 在创建Git版本库时,Git会为我们⾃动创建⼀个唯⼀的master分⽀,以及指向master的⼀个指针叫HEAD。
- 当对⼯作区修改(或新增)的⽂件执⾏git add命令时,暂存区⽬录树的⽂件索引会被更新。
- 当执⾏提交操作 git commit 时,master分⽀会做相应的更新,可以简单理解为暂存区的⽬录树才会被真正写到版本库中。
由上述描述我们便能得知:通过新建或粘贴进⽬录的⽂件,并不能称之为向仓库中新增⽂件,⽽只是在⼯作区新增了⽂件。必须要通过使⽤git add和git commit命令才能将⽂件添加到仓库中进⾏管理!!!
新增文件
在包含.git的⽬录下新建⼀个ReadMe⽂件,我们可以使⽤ git add 命令可以将⽂件添加到暂存区,
可以在git add后面,添加一个或者多个文件,又或者可以添加指定目录,还可以使用 . 指令添加当前目录下的所有文件到暂存区。然后可以使用 git commit 指令将暂存区的内容添加到本地仓库中。需要注意的就是 git commit 后面的-m选项一定要添加,用于描述本次提交的信息,并且需要好好地描述提交细节。
添加完文件之后还可以使用 git log 指令查看历史提交记录,如果输出信息太多就可以在指令中添加上--pretty=oneline参数。
然后再次查看.git文件就会发现多出了一些内容:
index就是暂存区,add后的内容都是添加到这里的HEAD就是默认指向master分支的指针。
在上述图片中master中存放的168b3de87207f10b5296a3cd9694e6152de82782 就是当前最新的commit id 在object库中我们也可以查看到这些对象。查找object 时要将 commit id 分成2部分,其前2位是⽂件夹名称,后38位是⽂件名称。找到这个⽂件之后,⼀般不能直接看到⾥⾯是什么,该类⽂件是经过 sha (安全哈希算法)加密过的⽂件,好在我们可以使⽤ git cat-file 命令来查看版本库对象的内容,这样就查看到了我们最近一次的提交记录,对其中的tree后面这一行再次进行同样的操作,再次查看ReadMe对应的数字,可以发现我们对于ReadMe的修改已经被保存了下来
修改文件
git追踪管理的不是文件,而是修改。这里的修改指的不仅仅是修改,而是新增或者删除。首先我们对ReadMe文件进行修改减少一行,并添加一行。
此时,仓库中的ReadMe和我们⼯作区的ReadMe是不同的,如何查看当前仓库的状态呢? 使用 git status 命令⽤于查看在你上次提交之后是否有对⽂件进⾏再次修改。
但是这个指令只能够告诉我们ReadMe文件被修改过了,但是还没有完成添加与提交。这时可以使用指令git diff [file] 来得知具体那些文件进行了修改,也可以使用 git diff HEAD -- [file] 命令来查看版本库与工作区文件的区别。
版本回退
执⾏ git reset 命令⽤于回退版本,可以指定退回某⼀次提交的版本。要解释⼀下“回退”本质是要将版本库中的内容进⾏回退,⼯作区或暂存区是否回退由命令参数决定:
git reset [--soft | --mixed | --hard] [HEAD]
工作区 | 暂存区 | 版本库 | |
-- soft | 不回退 | 不回退 | 回退 |
-- mixed(默认选项) | 不回退 | 回退 | 回退 |
-- hard(慎用) | 回退 | 回退 | 回退 |
HEAD 说明:
可直接写成commit id,表⽰指定退回的版本
- HEAD表⽰当前版本
- HEAD^上⼀个版本
- HEAD^^上上⼀个版本
- 以此类推...
可以使⽤〜数字表⽰:
- HEAD~0表⽰当前版本
- HEAD~1上⼀个版本
- HEAD^2上上⼀个版本
- 以此类推...
此时如果我们如果打印ReadMe中的信息就是第二次提交对应的信息,此时若是我们后悔了想要撤回,也可以同样使用git reset 指令找到modify ReadMe 2 这个日志前面的commit id即可进行回退。这里能够后悔因为我们能够找到modify ReadMe 2前的commit id,但是如果我们失去了这些数字,此时打印日志也没有显示我们想要的,就可以使用git reflog 指令,这条指令会记录本地的每次提交命令,前面的数字就是commit id的一部分,我们可以使用它来进行回退。
再次使用git reset 指令[commit id]即可回退至我们想要的版本。
撤销修改
撤销的目的就是不影响远程仓库中的代码,有时我们可能觉得代码写的不太好想要撤销我们的修改,对于撤销有三种情况:
工作区 | 暂存区 | 版本库 | 解决方案 |
已修改未add | 1. 手动撤销 2. git checkout -- [file] | ||
已修改 | 已经add未commit | git reset -- mixed HEAD [file] (将暂存区中的内容回退至指定版本内容) | |
已修改 | 已经add | 已经commit | 前提条件:commit之后没有push git reset -- hard HEAD^ [file] |
情况⼀:对于⼯作区的代码,还没有 add
Git其实为我们提供了更好的⽅式,我们可以使⽤ git checkout -- [file] 命令让⼯作区的⽂件回到最近⼀次 add 或 commit 时的状态。要注意 git checkout -- [file] 命令中的-- 很重要,切记不要省略,⼀旦省略,该命令就变为其他意思了。
情况⼆:已经 add ,但没有 commit
这是可以使用git reset 将工作区的内容回退至版本库中的内容。
情况三:已经 add ,并且也commit 了。
使用之间版本回退中学习的指令即可,但是这有这一个前提条件:commit之后没有push到远程仓库中。
删除文件
因为删除同样是修改的一种,那么就可以先进行rm删除,然后add,最后commit即可完成删除。同样在git中也为我们提供了删除,使用git rm 就可以删除工作区和暂存区的文件,然后进行一次commit即可。
分支管理
之前我们学习的git的基本操作都是在master分支上进行的操作,其中HEAD指向master,master指向最新的一次提交,每次提交master分支都会向前移动一步,指向我们最新的一次提交。有时我们在编写代码的时候为了防止出现bug,不想直接在master分支上进行修改,此时我们就可以基于master分支新建一个分支,在这个分支上进行代码的编写,等到测试过之后在合并到master分支上。
创建分支
Git⽀持我们查看或创建其他分⽀,在这⾥我们来创建第⼀个⾃⼰的分⽀ dev,对应的命令为:
查看当前本地所有分支: git branch
创建分支: git branch [分支名]
由上图可以看出新创建一个dev分支,*表示当前的HEAD指向的是master分支 ,同时也可以看到在.git的文件中存在了dev这个分支。
切换分支
如果需要进行切换分支,就可以使用指令:
切换分支: git checkout dev
新建分支并切换: git checkout -b dev
合并并切换之后就是上述的样子。然后我们就可以在分支上进行开发了:
首先我们在dev分支下对RendMe文件进行编辑,进行提交之后,可以对文件cat查看文件内容确实存在,然后我们再次切回master分支上,却发现ReadMe文件中新增的内容不见了。通过查看dev分支和master分支指向,发现两者的提交是不一样的。
因为我们是在dev分⽀上提交的,⽽master分⽀此刻的提交点并没有变,此时的状态如图如下所⽰,当切换到master分⽀之时,HEAD就指向了master,当然看不到提交了。
合并分支
对于上述的例子,为了能够在master主分支上看到dev分支上的提交,就需要将dev分支合并到master分支使用指令:
在master分支上合并dev分支新增的内容: git merge dev
Fast-forward代表“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度⾮常快。
当然,也不是每次合并都能Fast-forward,可能会出现合并冲突。
删除分支
合并完成后,dev分⽀对于我们来说就没⽤了,那么dev分⽀就可以被删除掉,注意如果当前正处于某分⽀下,就不能删除当前分⽀。
需要我们在其他分支下来删除当前分支。 因为创建、合并和删除分⽀⾮常快,所以Git⿎励你使⽤分⽀完成某个任务,合并后再删掉分⽀,这和直接在master分⽀上⼯作效果是⼀样的,但过程更安全。
合并冲突
当我们基于master新建了一个分支dev1在里面进行文件的修改并提交,然后在master分支中对同一个文件进行修改并提交,此时如果我们进行合并就会出现冲突。
发现ReadMe⽂件有冲突后,可以直接查看⽂件内容,要说的是Git会⽤<<<<<<<,=======,>>>>>>>来标记出不同分⽀的冲突内容,如下图所示:
此时我们必须要手动调整冲突代码,并需要再次提交修正后的结果。
分支管理策略
通常合并分⽀时,如果可能,Git会采⽤ Fast forward 模式。在这种 Fast forward 模式下,删除分⽀后,查看分⽀历史时,会丢掉分⽀信息,看不出来最新提交到底是merge进来的还是正常提交。
但在合并冲突部分,我们也看到通过解决冲突问题,会再进⾏⼀次新的提交,得到的最终状态就如上图所示。那么这就不是 Fast forward 模式了,这样的好处是,从分⽀历史上就可以看出分⽀信息。我们现在已经删除了在合并冲突部分创建的 dev1 分⽀,但依旧能看到master,其实是由其他分⽀合并得到。使用下图中的指令可以打印相关的信息:
git log --graph --pretty=oneline --abbrev-commit
git支持我们强制禁用 Fast forward模式,此时既可以在merge时生成一个新的commit,可以从分支历史上看出分支信息。首先我们在master分支的基础上拉取dev2分支,在dev2分支分支上的ReadMe文件进行修改,然后切换回master分支进行合并,正常直接时候git merge指令就会使用 Fast forward模式,但是此时我们想换一种方式,就可以使用指令:
git merge --no-ff -m "merge whit no-ff" dev2
此时在merge时就会需要我们生成一个新的commit,从下图中就可以看出确实对merge进行了记录
bug 分支
假如我们现在正在 dev2 分⽀上进⾏开发,开发到⼀半,突然发现 master 分⽀上⾯有bug,需要
解决。在Git中,每个bug都可以通过⼀个新的临时分⽀来修复,修复后,合并分⽀,然后将临时分⽀删除。可现在 dev2 的代码在⼯作区中开发了⼀半,还⽆法提交,怎么办,如下图所示,我们可以使用git提供的git stash指令,可以将当前的⼯作区信息进⾏储藏,被储藏的内容可以在将来某个时间恢复出来。
储藏 dev2 ⼯作区之后,由于我们要基于master分⽀修复bug,所以需要切回 master 分⽀,再新
建临时分⽀来修复bug,在这里有一点需要我们记住就是,在fix_bug对master进行合并的时候可以现在fix_bug分支上进行合并减少冲突,然后再在master上进行合并。此时会更加的安全。就如下图所示:
简要的表示就如下图:
至此,bug就修复完成了,我们还需要重新回到dev2分支上进行开发,可以发现工作区是干净的,之前我们使用git stash将内容存储了起来,使用 git stash pop 指令可以恢复,然后就可以继续开发
Master 分⽀⽬前最新的提交,是要领先于新建 dev2 时基于的 master 分⽀的提交的,所以我们
在 dev2 中当然看不⻅修复bug的相关代码。
我们的最终⽬的是要让 master 合并 dev2 分⽀的,那么正常情况下我们切回 master 分⽀直接合
并即可,但这样其实是有⼀定⻛险的。是因为在合并分⽀时可能会有冲突,⽽代码冲突需要我们⼿动解决(在 master 上解决)。我们⽆法保证对于冲突问题可以正确地⼀次性解决掉,因为在实际的项⽬中,代码冲突不只⼀两⾏那么简单,有可能⼏⼗上百⾏,甚⾄更多,解决的过程中难免⼿误出错,导致错误的代码被合并到 master 上。解决这个问题的⼀个好的建议就是:最好在⾃⼰的分⽀上合并下 master ,再让 master 去合并dev ,这样做的⽬的是有冲突可以在本地分⽀解决并进⾏测试,⽽不影响 master。这就跟我们前面将的方法是一致的。
删除临时分支
如果我们要将开发了一段时间的分支删除,此时已经经过了add与commit。这时使⽤传统的 git branch -d 命令删除分⽀的⽅法是不行的
需要使用-D来删除。
远程操作
远程仓库
Git是分布式版本控制系统,同⼀个Git仓库,可以分布到不同的机器上。怎么分布呢?最早,肯定只有⼀台机器有⼀个原始版本库,此后,别的机器可以“克隆”这个原始版本库,⽽且每台机器的版本库其实都是⼀样的,并没有主次之分。
你肯定会想,⾄少需要两台机器才能玩远程库不是?但是我只有⼀台电脑,怎么玩?
其实⼀台电脑上也是可以克隆多个版本库的,只要不在同⼀个⽬录下。不过,现实⽣活中是不会有⼈这么傻的在⼀台电脑上搞⼏个远程库玩,因为⼀台电脑上搞⼏个远程库完全没有意义,⽽且硬盘挂了会导致所有库都挂掉,所以我也不告诉你在⼀台电脑上怎么克隆多个仓库。
实际情况往往是这样,找⼀台电脑充当服务器的⻆⾊,每天24⼩时开机,其他每个⼈都从这个“服务器”仓库克隆⼀份到⾃⼰的电脑上,并且各⾃把各⾃的提交推送到服务器仓库⾥,也从服务器仓库中拉取别⼈的提交。Github提供Git仓库托管服务的。这里我们使用的是gitee。
新建远程仓库
我们可以在gitee的网页上新建远程仓库。
克隆远程仓库
SSH协议和HTTPS协议是Git最常使⽤的两种数据传输协议。SSH协议使⽤了公钥加密和公钥登陆机制,体现了其实⽤性和安全性,使⽤此协议需要将我们的公钥放上服务器,由Git服务器进⾏管理。使⽤HTTPS⽅式时,没有要求,可以直接克隆下来。使⽤SSH⽅式克隆仓库,由于我们没有添加公钥到远端库中,服务器拒绝了我们的clone链接。需要我们设置⼀下:第⼀步:创建SSHKey。在用户主⽬录下,看看有没有.ssh⽬录,如果有,再看看这个⽬录下有没有id_rsa 和 id_rsa.pub 这两个⽂件,如果已经有了,可直接跳到下⼀步。如果没有,需要创建SSH Key:
注意要输⼊⾃⼰的邮箱,然后⼀路回⻋,使⽤默认值即可。
顺利的话,可以在⽤⼾主⽬录⾥找到 .ssh ⽬录,⾥⾯有 id_rsa 和 id_rsa.pub 两个⽂件,这两
个就是SSH Key的秘钥对,id_rsa 是私钥,不能泄露出去,id_rsa.pub 是公钥,可以放心地告
诉任何人。将自己的公钥添加到公钥远程仓库即可。
当我们从远程仓库克隆后,实际上Git会⾃动把本地的master分⽀和远程的master分⽀对应起来,
并且,远程仓库的默认名称是 origin 。在本地我们可以使⽤ git remote 命令,来查看远程库的
信息。
向远程仓库推送
git push <远程主机名> <本地分⽀名>:<远程分⽀名>
# 如果本地分⽀名与远程分⽀名相同,则可以省略冒号:
git push <远程主机名> <本地分⽀名>
推送成功!这⾥由于我们使用的是SSH协议,是不⽤每⼀次推送都输⼊密码的,⽅便了我们的推送操作。如果你使⽤的是HTTPS协议,有个麻烦地⽅就是每次推送都必须输⼊⼝令。
拉取远程仓库
git pull <远程主机名> <远程分⽀名>:<本地分⽀名>
# 如果远程分⽀是与当前分⽀合并,则冒号后⾯的部分可以省略。
git pull <远程主机名> <远程分⽀名>
配置 Git
忽略特殊文件
在⽇常开发中,我们有些⽂件不想或者不应该提交到远端,比如保存了数据库密码的配置⽂件,那怎么让Git知道呢?在Git⼯作区的根目录下创建⼀个特殊的 .gitignore ⽂件,然后把要忽略的⽂件
名填进去,Git就会⾃动忽略这些⽂件了。例如我们想忽略.so结尾的所有文件,就可以在.gitignore文件中写入*.so。从下图中就可以看出1.so这个文件被忽略了。
检验.gitignore的标准就是git status命令会显示work directory clean,在工作区中没有文件新增。
但有时候需要添加一个文件,但是这个文件被忽略了,那么就可以通过 -f 强制添加
git add -f [filename]
或者你发现,可能是 .gitignore 写得有问题,需要找出来到底哪个规则写错了,⽐如说a.so⽂件
是要被添加的,可以⽤ git check-ignore 命令检查:
我们还可以再文件前添加!+ 文件名,就可以将这个文件设置为例外。
给命令配置别名
在我们使⽤Git期间,有些命令敲的时候着实让⼈头疼,幸运的是,git⽀持对命令进⾏简化!
举个例⼦,将 git status 简化为 git st ,对应的命令为:
git config --global alias.st status
标签管理
标签 tag ,可以简单的理解为是对某次 commit 的⼀个标识,相当于起了⼀个别名。例如,在项⽬发布某个版本的时候,针对最后⼀次 commit 起⼀个 v1.0 这样的标签来标识⾥程碑的意义。这有什么⽤呢?相较于难以记住的 commit id , tag 很好的解决这个问题,因为 tag ⼀定要给⼀个让⼈容易记住,且有意义的名字。当我们需要回退到某个重要版本时,直接使⽤标签就能很快定位到。
创建标签
在Git中打标签⾮常简单,⾸先,切换到需要打标签的分⽀上,然后,敲命令 git tag [name] 就可以打⼀个新标签。⽤命令 git tag 查看所有标签。默认标签是打在最新提交的 commit 上的。那如何在指定的commit上打标签呢?⽅法是找到历史提交的commit id,然后打上就可以了。
Git还提供可以创建带有说明的标签,⽤-a指定标签名,-m指定说明⽂字,格式为:
git tag -a [name] -m "XXX" [commit_id]
操作标签
如果标签打错了,也可以删除。使用指令:
git tag -d <tagname>
因为创建的标签都只存储在本地,不会⾃动推送到远程。所以,打错的标签可以在本地安全删除。如果要推送某个标签到远程,使⽤命令:
git push origin <tagname>
当然,如果你本地有很多标签,也可以⼀次性的全部推送到远端:
git push origin --tags
如果标签已经推送到远程,要删除远程标签就⿇烦⼀点,先从本地删除:
git tag -d v1.0
然后,从远程删除。删除命令也是push,但是格式如下:
git push origin :refs/tags/v1.0