Git 简介
什么是Git?
Git是一个免费开源的分布式版本控制系统,起源于2005年,最初目的是为更好地管理Linux内核开发而设计的, 与CVS、Subversion一类的集中式版本控制工具不同,它采用了分布式版本库的作法, 每个客户端本地都保存有完整的历史亡信息,不需要服务器端软件,就可以运作版本控制,使得源代码的发布和交流极其方便。
git的速度很快,这对于诸如Linux内核这样的大项目来说自然很重要。 git最为出色的是它的合并追踪(merge tracing)能力。
如何安装Git?
Windows
点击Git官网下载安装
Linux
Linux 发行版不一样,安装方式也不一样,常见的apt命令和yum命令安装方式如下
# 使用apt命令
apt install git -y
# 使用yum命令
yum install git
Linux更多安装方式请参考:Git官网Linux安装教程
MacOS
打开终端,输入git命令回车,会自动弹出安装xcode命令行工具的窗口,按提示安装就行。如果没有弹出窗口可以使用下面的命令手动安装。
xcode-select --install
基础操作
基础操作介绍了git的基本克隆,切换分支, 提交,推送,拉取等操作,如果这些你已经会了, 可直接跳过看下面的进阶操作。
克隆仓库
git clone 仓库地址 本地目录名 执行上面的命令git会把对应的仓库地址的内容克隆到本地指定的目录中,指定的目录名可以不传, 默认在当前目录下使用仓库名做为目录名。
查看分支
git branch 执行该命令可以查看当前git仓库下的所有分支,然后使用下面的命令切换到对应的分支进行开发
切换分支
git checkout 分支名
一般git仓库克隆下来默认是master分支,可以使用该命令切换到开发分支或当前的版本分支。
如何使用指定commitid检出分支请看下面进阶操作里面的“创建分支”
查看文件状态
git status
该命令可以查看当前git仓库的状态信息,新增,修改或者删除了哪些文件。在合并分支出现冲突时也可以使用status命令查看冲突的文件,会看到有文件的状态是"both modified"。
如果需要详情查看哪些行冲突,请看进阶操作中的“查看冲突”。
忽略文件
仓库下并非所有的文件都需要由git跟踪其变化,比如NodeJs的模块安装目录node_modules, 程序运行产生的日志文件等等。这时就需要让git跟踪文件变化时忽略这些文件。
方法是使用".gitignore"文件来配置要忽略的文件,比如下面的.gitignore文件
# 忽略所有以.log结尾的文件
*.log
# 忽略所有tmp目录及其下面的文件
tmp/
# 忽略根目录下的/build/目录及其下面的文件
/build/
# 忽略所有目录下名为test.c的文件
test.c
.gitignore文件可以放到仓库根目录下, 这样整个仓库所有的目录都会应用忽略规则。
也可以放到仓库中其他任何目录下,这样git会应用当前目录下的忽略规则的同时一级一级向上查找并应用上级目录的忽略规则直至仓库根目录。
暂存更改
git add 文件匹配模式
# 暂存当前目录下所有更改
git add .
# 暂存所有cpp文件
git add *.cpp
# 暂存src目录下所有更改
git add src
如果在git仓库目录下添加了新文件,执行了文件修改,使用该命令把修改保存到暂存区,再使用下面的命令提交更改
提交更改
# 提交代码
git commit -m "提交说明"
# 暂存并提交代码, 使用-a参数可以在只有更改文件的情况下不执行git add, 直接提交代码
git commit -am "提交说明"
# 忽略git hooks暂存并提交代码
git commit -am "提交说明" --no-verify
在推送更改到服务器之前要先提交代码,使用该命令可以把更改提交到git中,提交代码后使用下面的使用把更改推送到服务器。
关于Git Hooks的内容请看下面高阶操作中的"Git Hooks"
推送更改
git push
执行上面提交代码的步骤后只是在本地的git仓库保存了更改, 但协同工作的其他人并不能看到你提交的代码,执行push命令后可以把你本地的更改提交到服务器中, 其他人使用下面的命令可以拉取你的更改。
在协作开发中,会出现别人已经在你之前推送了代码,这时候你再推送就会不成功, 可使用下面的命令先拉取一次再执行推送操作
拉取更改
git pull
执行该命令可以从服务器上拉取别人提交的代码并合并到你本地的代码中,在合并代码的过程中可能会遇到多个人更改了同一个文件的情况,这种情况下代码合并时冲突,冲突解决方法请看下面进阶操作里面的“冲突解决”。
标签管理
# 基于当前分支创建新标签
git tag 标签名
# 删除一个标签
git tag -d 标签名
在一个版本完成所有的开发,测试和验收合并到主干之后,会基于主干最新的分支创建一个标签,这个标签是只能的,其内容不可更改,方便溯源。
进阶操作
创建分支
# 基于当前分支创建新分支
git branch 分支名
# 基于当前分支创建并立即切换到新分支
git checkout -b 分支名
# 基于指定commitid创建新分支
git checkout commitid -b 分支名
随着项目的版本迭代和bug修复,总会有新建各种分支的需要。该命令可以基于当前分支在本地创建一个新的分支。创建新的分支后使用下面的“推送新分支到服务端”命令可以把新的分支推送到服务器端,这样别人再拉取的时候可以把新分支一并拉取到本地。
其实创建分支和上面切换分支是同一个命令,或分支存在运行该命令是切换分支,或分支不存在则基于当前分支创建新分支。
推送新分支到服务端
git push --set-upstream 远程名 分支名
在本地新建了分支以后如果push会提示“fatal: The current branch test has no upstream branch.”,这时候需要使用--set-upstream参数把本地的分支和服务器上的分支关联起来。
如果没有手动添加远程地址的话,远程名默认为origin,分支名可以写当前分支名, 也可以写其他的任何名称。
合并分支
# 先切换到要合并的分支
git checkout master
# 把分支名合并到当前分支
git merge 分支名
除了mater分支外一般其他分支是不单独存在的,在版本开发测试完成后会把迭代分支合并到master分支,bug修复完成后也会把bug修复分支合并到对应的迭代分支然后再合并到master分支中。
冲突解决
团队开发总是会不可避免的会出现两个甚至多个人在同一个提交或在合并代码时修改了同一个文件的情况,这时就会造成文件冲突。
存在无法自动解决的文件冲突时会出现类似下面这种提示
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
上面的index.html就是冲突的文件,git会把多方的更改都保存到对应的文件中,并以分割线分割开各个修改,其中<<<<<<< HEAD和=======之间的内容表示当前分支的更改,=======和>>>>>>> newMaster之间的内容表示来自newMaster分支的更改。把需要的更改保留,不需要的更改删除,然后使用git add 命令暂存更改。然后就可以正常的提交和推送了。
<<<<<<< HEAD
<title>from master</title>
=======
<title>from new master112</title>
>>>>>>> newMaster
查看冲突
git diff --check
在合并分支时如果冲突的文件非常多,或者某个文件内冲突的行数非常多,处理的时候可以会漏掉一些内容。这里可以使用diff命令查看还有哪些文件的哪些行还没有处理的冲突。
取消合并
git merge --abort
在合并分支后发现合错了,这时候可以使用merge --abort取消当前合并请求。
删除分支
# 本地分支中没有未推送的提交
git branch -d 分支名
# 不检查是否推送, 直接删除本地分支
git branch -D 分支名
# 删除本地和远程分支
git branch -d -r 分支名
在一个漏洞修复分支或需求开发分支合并到主干后使用branch -d可以把开发或修复分支删除。有时候需要测试一个方案的可选性可能会在本地拉一个临时分支,测试完毕后也可以使用该命令把临时分支删除。
查看提交记录
# 查看当前分支提交日志
git log
# 以树形查看所有分支日志
git log --all --graph
# 查看分支合并记录
git log --merges
使用log命令查看git已跟踪的修改记录,方便溯源。
高阶操作
再次提交
git cherry-pick commitid
在有些时候,比如在某个版本修复一个严重漏洞,需要立即把已经发布的其他版本的同一个漏洞也都修复了,这个时候只需要切换到对应版本的分支,把修复漏洞的commit重新提交到当前分支就可以了,而不必把每个分支都改一遍。
撤回提交
# 撤销上一个commit,但不撤销add
git reset --soft HEAD^
# 撤销到N个commit,但不撤销add
git reset --soft HEAD~N
# 撤销上一个commit和add,但不删除代码
git reset --mixed HEAD~1
# 撤销上一个commit和add并删除代码
git reset --hard HEAD^
提交后发现提交错了,可以使用reset撤销提交。HEAD^和HEAD~1作用一样,都是撤销上一个提交,撤销多个提交用HEAD~N, N是要撤销的提交数。
查看提交详情
git show commitid或分支名或标签名
使用show可以查看某个分支或标签的最后一次提交详情,如果show后面跟的是commitid,则打印该commitid对应的提交的详情,打印内容包括commitid,对应的分支或标签名,提交人,提交时间,提交注释和文件变动。
打包仓库
# 打包所有的分支
git bundle create 打包文件名 --branches
# 打包所有的标签
git bundle create 打包文件名 --tags
# 打包所有的分支和标签
git bundle create 打包文件名 --all
# 校验已打包的文件
git bundle verify 打包文件名
# 列出打包文件内所有的分支和标签
git bundle list-heads 文件名
# 解包已打包的文件
git bundle unbundle 打包文件名
# 从已打包的文件中检出git仓库到目录名中
git clone 打包文件名 目录名
把整个仓库打包,打包以后可以像普通的远程仓库一样检出,一般用于备份或在无网络情况下协同开发。
远程服务器管理
# 查看已配置的远程服务器
git remote -v
# 添加一个远程服务器
git remote add 名称 URL
# 修改一个已存在的远程服务器名称
git remote rename 老名称 新名称
# 删除一个指定的远程服务器
git remote remove 名称
# 获取指定远程服务器的URL
git remote get-url 名称
# 设置远程服务器地址
git remote set-url 名称 新URL
在代码从一个远程仓库检出后,默认会存在一个名为origin的远程服务器。大部分情况下我们是不需要管理远程服务器的,在远程仓库地址变化时需要重新设置远程地址。还有些时候需要将代码推送到两个不同的服务器,可以使用上面的添加远程服务器命令添加一个新的服务器。
合并两个不同仓库
# 不检查历史记录,强制拉取
git pull --allow-unrelated-histories
# 不检查历史记录,强制从origin服务器的master分支拉取
git pull origin master --allow-unrelated-histories
有时候一个本地已经写了好久的项目,想要上传到git服务器里面指定仓库中。 这里可以使用git init 在项目目录下初始化一个git仓库,然后使用git remote add origin url添加远程服务器, 使用git pull的时候会发现报错“fatal: refusing to merge unrelated histories”, 这是由于本地的提交记录和服务器的不一致导致的,这时可以使用--allow-unrelated-histories参数忽略历史记录检查。
未完待续