简介
Git是目前世界上最先进的分布式版本控制系统,由C语言进行开发 在2022年之前,Linux构建的方式是世界各地的志愿者把源代码文件通过diff的方式发送给Linus,然后由Linus本人通过手工方式合并代码 Linus痛恨的CVS和SVN都是集中式的版本控制系统,而Git是分布式的版本控制系统,这两者有何区别? 集中式:版本库是集中存放在中央服务器的,开发的时候用的都是开发机(本地机器),所以要先从中央服务器获取最新的版本,然后进行开发之后将代码推送给中央服务器。最大的问题是必须联网才能够进行工作,在网速慢的时候,提交大文件就会非常耗时。 分布式:分布式控制版本是没有“中央服务器”这个概念的,每个人的电脑上都是一个完整的版本库,这样在开发的时候就不需要联网,因为版本库在自己的电脑上。既然每个人的电脑上都有一个完整的版本库,那多个人如何协作呢?这时候就需要将各自对文件的修改推送给对方,就可以互相看到对方的修改了。和集中式版本控制系统相比,分布式版本控制的安全性会高很多。因为每个人的电脑里都有完整的版本库,某个人的版本库出问题了没有关系,而集中式版本控制只有一个版本库。通常分布式版本控制系统也有一台充当“中央服务器”的电脑,当然这个服务器的作用只是方便“交换修改”,没有它也一样能够干活,只是交换修改不方便。
Git与svn的区别
-
历史记录:Git更加轻量级,每次提交只记录变化,而SVN每次提交都会存储完整的文件 -
版本管理:Git更加灵活,允许分支和分支合并,而SVN只有主干 -
安全性:Git分布式存储,一个服务器挂掉不会影响其他服务器,而SVN单一服务器容易出现安全问题 -
开发流程:Git的开发流程更加快捷,可以快速的实现拉取、提交,而SVN开发流程繁琐 -
部署:Git无需安全客户端,支持跨平台,而SVN必须安装客户端才能使用 -
使用:Git更加简单,学习成本更低,而SVN略显复杂
操作
创建版本库
版本库:仓库repository,可以简单理解为一个目录,这个目录中的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能够进行跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。 创建一个版本库很简单
//1.创建目录 mkdir learngit cd learngit pwd
//2.通过git init将这个目录变成Git可以管理的仓库 执行完git init命令之后,当前目录会多出一个.git目录 这个目录是git用来跟踪版本库的,默认是一个隐藏文件
//3. 将文件放到Git仓库中 vim readme.txt //随便写点什么 git add readme.txt //将readme.txt文件添加到仓库 git commit -m "this is a readme.txt"
为什么add和commit要分成两步呢?因为一次commit可以提交很多文件,而可以多次add不同的文件
状态查看
现在我们将readme.txt文件修改为如下内容 Git is a distributed version control system. Git is free software. 可以通过git status 来查看结果 git status 告诉我们readme.txt文件已经被修改了,但是还没有准备提交的修改 怎么查看到底该了什么内容呢? 可以通过git diff命令来查看difference,显示的格式正是Unix通用的Diff格式 通过git add readme.txt进行提交 再通过git status查看当前状态 这是告诉我们,被提交的修改有readme.txt文件
-
如果想要查看工作区的状态,使用Git status命令 -
如果git status告诉你有文件被修改过,用git diff可以查看修改内容
回退
我们再次修改readme文件
以下是新内容 Git is a distributed version control system. Git is free software distributed under the GPL.
之后尝试提交 像这样,不断对文件进行修改,然后不断提交修改到版本库里,就类似于把git的状态存盘,每当文件修改到一定程度的时候,就可以『保存一个快照』,这个快找在Git中被称为commit
,一旦把文件弄乱了或者误删了文件,可以从最近的一个commmit
中恢复 git log
能够帮助我们看到git的历史记录 显示的是从最近到最远的提交日志,如果嫌输出信息太多,可以加上--pretty=online
参数 在Git中,用HEAD
来表示当前版本,上一个版本为HEAD^
,上上一个版本为HEAD^^
, 我们可以通过git reset --hard HEAD^
来将文件回退到上一个版本 在底层,Git在内部有一个指向当前版本的HEAD
指针,当进行回退版本的时候,GIT仅仅是将HEAD的指向进行了改变 一个新的问题:如果这个时候我又想回到GPL的那个版本该怎么办呢? 可以看到这时候我们通过Git log进行日志查看,已经没有那个版本的记录了 我们可以通过git reflog 看到之前提交的id 通过git reset --hard参数+ID 进行回退
工作区与暂存区
Git与其他版本控制系统的一个不同之处就是有暂存区的概念 工作区 就是电脑里能够看到的目录,比如learngit文件夹 版本库 工作区有一个隐藏目录.git,这个不算是工作区,而是Git的版本库。Git的版本库里面有很多东西,其中最重要的就是称为stage(index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫做HEAD 我们将文件往Git版本库添加的时候,是分两步执行的
-
git add把文件添加进去,实际上就是把文件添加到暂存区 -
git commit提交更改,实际上就是把文件去的所有内容提交到当前分支
因为在创建GIT版本库的时候,GIT自动为我们创建了唯一一个分支master,所以git commit就是往master分支上提交更改 git commit之后
管理修改
每一次的修改都必须使用git add添加之后,才会放到暂存区,在git commit的时候才会提交到分支。如果修改了之后没有使用git add命令,那么是不会提交到分支中的
撤销修改
当我们将文件修改成以下内容,但是未提交的时候,git会进行以下提示 Git is free software distributed under the GPL. Git has a mutable index called stage. Git tracks changes of files. My stupid boss still prefers SVN. 可以使用git checkout -- readme.txt 进行把readme.txt文件的工作区的修改全部撤销 切记,一定要添加--标签,不然这个命令会变成切换到另一个分支 这里的撤销有两种情况
-
readme.txt自修改之后还没有被放到缓存区中,现在,撤销修改就回到和版本库一模一样的状态 -
readme.txt已经添加到暂存区之后,又做了修改,撤回修改就回到添加到暂存区之后的状态
当然 现在推荐的命令是 git restore readme.txt 当已经把修改通过git add
命令添加到暂存区之后,想要丢弃修改,分成两步
-
git reset HEAD -
git checkout --file
删除文件
当我们在工作区下添加一个文件 vim test.txt 内容如下: hello world. 然后通过git add test.txt 与 git commit -m "add test.txt"进行提交 这个时候我们把test.txt文件进行删除 再通过git status查看会发现
现在我们有两种选择 1.确定删除 使用git rm test.txt,然后git commit -m "remove test.txt" 2.删错了 使用版本库里的版本替换工作区的版本 git checkout -- test.txt
远程仓库
添加远程仓库 git remote add origin git@xxx
git push //将本地库的内容推送到远程库上 -u 参数不但能够实现远程推送 还能够将本地分支与远端分支关联起来 之后的推送或者拉取就能够简化命令 git remote -v
查看远程仓库信息 git remote rm origin
删除远程仓库(解除本地和远程的绑定关系) git clone
克隆远端代码 ssh协议的速度>https
分支管理
HEAD是一个指针,指向的分支就是当前分支,在一开始的时候,master分支是一条线,Git使用master指向最新的提交,再用HEAD指向master,就能确定当前分支以及提交点 每次提交,master分支都会向前移动一步,这样随着不断提交,master分支的线也会越来越长 当我们创建了一个新的分支,其实也就是一个新的指针dev,指向的是master相同的提交,再把HEAD指向dev,表示当前分支在dev上 因为git的新建分支是创建一个指针以及修改HEAD的指向,而文件本身内容不变,所以速度很快 从现在开始,对工作区的修改和提交就是针对dev分支了,新提交之后dev指针向前移动,但是master指针不变
分支管理
git branch dev 创建dev分支 git checkout dev 切换到dev分支 添加一个内容为hello world的test文件 git add + git commit 提交成果 git checkout master 切换回主分支 git merge dev 将dev分支合并到当前分支 然后再通过git branch -d dev将dev分支进行删除 git推荐现在的分支操作使用switch命令 git switch -c
创建并切换到新分支 git switch master
创建到已有分支
冲突
git使用<<<<<<<,=======,>>>>>>>标记出不同分支的内容标记出不同的分支 更改冲突文件为想要的内容后提交
分支管理
在合并分支的时候,如果可能,git会使用fast forward
模式,在这种模式下,删除分支之后就会丢掉分支信息 合并分支的时候,加上--no-ff参数就可以使用普通模式合并,合并后的历史有分支,能够看出来曾经做过合并 git merge --no-ff "merge dev" dev
bug
一般每个bug都会新建一个分支来修改,修复之后合并分支,然后将临时分支删除 git stash 将当前工作区进行存储 一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除; 另一种方式是用git stash pop,恢复的同时把stash内容也删了:
因此,多人协作的工作模式通常是这样:
-
首先,可以试图用git push origin 推送自己的修改; -
如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并; -
如果合并有冲突,则解决冲突,并在本地提交; -
没有冲突或者解决掉冲突后,再用git push origin 推送就能成功!
如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to origin/ 。 这就是多人协作的工作模式,一旦熟悉了,就非常简单。
标签
git tag 给commit打标签 git show 查看标签信息 git tag -a 查看所有标签 git tag -d 删除标签 git push origin 推送本地标签
github
1.fork一个仓库 然后clone仓库 因为没权限clone原仓库 2.开发 往自己的仓库推送 3.推送pr给原仓库
Git进阶操作
git pull
拉取成功但不更更新
这个因为本地有更改,和仓库对应不上,解决方式如下 1.git stash将本地修改存储起来 2.git pull //建议使用完整的git pull origin branchname
git pull内部执行原理
pull包含两个操作,fetch和merge fetch: 将远程仓库拉取到本地仓库 merge:将本地仓库和分支进行merge git pull的时候会向远端发送git-upload-pack请求,携带的是本地仓库commit的记录,如果一致则不需要拉取,不一样就将远端仓库拉下来
push之前pull的原因
git commit的时候,仓库并不会将本地和远程仓库代码进行比较,不会识别出代码是否存在冲突,必须进行pull命令之后,才会将本地代码和远程仓库的代码进行比较,如果二者的代码存在冲突,必须要解决冲突后重新commit push,如果不存在冲突,则pull的时候直接合并代码,不会将本地代码覆盖掉
git status中的untracked file
在Git中,未被跟踪的文件(untracked file)是指存在与Git管理的目录中,但是尚未被添加到Git版本控制中的文件。这些文件没有被Git追踪,因此它们不受Git版本控制的管理。 当使用git status
命令查看git存储库的状态,一般日志文件就会是untracked file,因为没必要对日志进行追踪,日志也不会提交到代码库中 如果想把未被追踪的文件添加到Git版本控制中,可以使用git add命令将它们添加到Git的暂存区中,然后使用git commit提交到存储库中
git add提交了多余的文件,并且已经git commit了,怎么撤销
如果只是git add了,但是还没有git commit ,也就是说这些文件只是添加到了暂存区,还没有进行提交,那么可以通过git reset + filename,将文件从暂存区中删除,或者git reset直接将所有文件都从暂存区中撤销 第一步得撤销git commit git revert sha值 撤销你的某一个提交 或者直接 git revert HEAD 撤销最近一次提交,并创建一个新的提交来记录这个撤销操作,这个操作可以用来修复一个错误的提交或者撤销一个不必要的提交 如果报错error:commit sha is a merge but no -m option was given 这是因为正在尝试通过git revert命令撤销一个合并提交,但是没有指定用于撤销的父提交。要解决这个问题,需要使用-m选项来指定父提交,该选项后面需要指定一个数字,标识用于撤销提交的父提交的编号 假设想撤销最近的一次合并提交,可以使用 git revert -m 1 HEAD
使用合并提交的第一个父提交来撤销该提交
git commit错分支了怎么办
1.git log命令查找刚刚提交的SHA值 2.git branch + git checkout 切换到你想提交的分支 3.git cherry-pick + sha 讲提交应用到当前分支
git revert后工作区代码消失
git reset --hard HEAD 该命令会将工作区和暂存区都重置为最新的提交,并清除所有未提交的修改。需要注意的是,这个命令会清除本地未提交的更改,因此在使用前请确认这些更改已经备份或者提交到了其他分支上。如果仍然无法恢复更改,可以使用git reflog命令查找之前的提交记录,使用git reset --hard +sha指向指定的提交
修改git commit提交信息
当我们cr被打回来的时候,就不需要重新git commit,而是直接git commit --amend修改之前的提交信息,这会被git认为是一次新的提交 1.git commit --amend 打开编辑器 2.修改提交信息 3.git push
查看git信息
1.git log 查看所有的提交历史记录 例如提交ID 作者 日期 提交说明等等,常用于查看Git仓库的历史提交记录以及对比和合并分支 2.git reflog 记录了Git仓库的每一次操作,包括分支和标签的创建、删除、移动等操作,这个命令可以用来找回已经删除的分支或者标签
git diff
1.git diff --cached 查看暂存区和最后一次提交之间的差异 也就是git add离上一次的更改 2.git diff filename 查看文件的更改 3.git diff 查看已经修改但是未暂存(没有add)的更改
工作区、暂存区、本地仓库、远程仓库
1.工作区:实际写代码的地方,电脑上的文件夹,里面放着我们的代码文件 2.暂存区:git提供的一个临时的存储取余,可以暂时存储我们修改过的文件,等待提交到本地仓库,对应的是某一个分支 3.本地仓库:存储代码版本历史记录的地方,可以看作是Git维护的一个数据库,存储了项目的所有历史版本 4.远程仓库:一个在网络上的Git仓库,通常由代码托管服务商提供,可以把本地仓库的代码推送到远程仓库中,也可以从远程仓库中拉取代码到本地仓库进行使用 基本工作流:通过git add将文件添加到暂存区,然后通过git commit提交到本地仓库,最后通过git push提交到远程仓库 为什么需要本地仓库:
-
安全性:本地仓库可以在本地保存代码的历史版本,即使在远程仓库数据丢失或者被破坏的情况下,本地仓库中的代码仍然是安全的 -
离线操作:没有网络的情况下,本地仓库允许开发人员继续对代码进行修改和提交 -
提高效率:由于本地仓库不需要每次从远程仓库拉取代码,可以大大减少代码拉取的时间和网络带宽
git工作区修改后切换到新分支,保存修改
如果在工作区有未提交的修改,并且切换到新分支,那么这些修改是不会被保存的,因为一个暂存区对应的是一个分支 这个时候我们就需要 1.git stash //将工作区的修改保存到一个临时区 2.git checkout 3.git stash pop 或者git stash apply 4.如果有冲突的话,处理对应的文件
<<<<<<< HEAD
// 这里是当前分支的修改内容
=======
// 这里是合并分支的修改内容
>>>>>>> merge-branch
二者区别 apply从stash中恢复最近的一次修改,但不会将这些修改移出 pop从stash中恢复最近的一次修改,同时将修改弹出
git merge和git rebase的区别
merge 是合并的意思 rebase 是复位基底的意思 推荐是使用git rebase 因为rebase的代码历史非常清晰
比如有一个master分支,同时6个人进行开发,需要创建六个单独的个人分支,然后使用merge的话就会有六个branch和主分支交织在一起,也就是master的commit历史是网状的.master是创建一个新的结点,然后将两个分支的历史联系在一起
而rebase会把提交移动到master的最前面,形成一条线
本文由 mdnice 多平台发布