简介
github和git是每个程序员都必须掌握的技能,在这里对两个工具作入门介绍。
github
github是代码托管平台,付费用户可以建立私人仓库,免费用户只能使用公共仓库,也就是代码要公开,它可以帮助开发者存储和管理其项目源代码。
-
网址:
https://github.com/
-
中文社区:
https://www.githubs.cn/
,从这里进入github网站会快点
github和git的关系:github使用git作为版本管理工具。
简单使用
新建仓库:
- 第一步:在浏览器中输入 github.com,进入github。因为是英文界面,所以需要有一定的英文基础。注册一个账号,需要提供自己的邮箱。
- 第二步:点击sign in,登录到自己的页面
- 第三步:点击左上角的New,新建一个仓库,建好后的仓库会在下方显示,点击就可进入。点击搜索框,可以搜索一些有用的项目
删除仓库:
- 第一步:进入仓库内部,点击setting,翻到最下面,有删除按钮
搜项目的技巧
这里介绍一些实际的使用案例:
- 搜索名称中包含sprint boot的项目:
in:name sprint boot
- 搜索收藏数量大于3000的项目:
stars:>3000
- 搜索分支数量大于100的项目 :
forks:>100
- 搜索readme中包含spring boot字眼的项目:
in:readme spring boot
- 搜索描述中包含spring boot字眼的项目:
in:description spring boot
- 搜索使用java语言实现的项目:
language:java
- 搜索最近的更新日期在2019年3月3日之后的项目:
pushed:>2019-09-03
git
简介
git是一个免费的、开源的分布式版本控制系统,诞生于2005年,起初是为了更好地管理Linux内核的开发而创立的。
版本控制
版本控制:对文件的修改做记录,相当于文件备份,用户可以在需要时将文件切换到以前的版本。版本控制是由个人开发过渡到团队协作过程中诞生出的产物,创建版本控制工具的目的就是为了方便多个开发者对同一个项目进行开发。
版本控制工具的需要提供的功能:
- 历史记录:将本地文件恢复到某一个历史状态
- 分支管理:允许团队在工作过程中多条生产线同时推进任务
- 权限控制
- 协同修改:多人并行修改同一个文件,手动合并冲突或依据修改顺序自动合并冲突
分布式版本控制:每个人都工作在通过克隆建立的本地版本库中,每个人都用于一个完整的版本库,所有操作都直接在本地完成而不需要网络连接
版本控制工具的历史
最早是使用diff和patch两个命令,后来是CVS和SVN
diff、patch
diff:以行为单位比较多个文件之间的差异,也可以比较文件夹
- 格式:diff [option …] files
patch:使用patch命令,根据diff命令生成的差异文件,来恢复原始文件。这个命令使用比较复杂,而且如今用处不大,所以只是了解一下即可
CVS
Concurrent Versions System,版本控制工具,诞生于1985年,是早期的一款版本控制工具
SVN
subversion,诞生于2000年方一款版本控制工具
安装和配置
下载地址:http://git-scm.com/downloads
。针对不同的平台,选择自己需要安装包
安装:下载完成后傻瓜式安装即可
配置git:在命令行执行如下命令
git config --global user.name "用户名"
:告诉git自己的用户名,–global选项,表示这台机器上的所有git仓库都会使用这个配置。如果去掉 --global 参数只对当前仓库有效。git config --global user.email "邮箱名"
:告诉git自己的邮箱git config --list
:显示当前git的配置信息
git的配置文件:
- 用户级别:用户目录下的 .gitconfig文件
- 全局级别:安装目录下的 etc/gitconfig文件
- 仓库级别:.git目录下的config文件,在使用git创建一个本地库后,会默认在本地库根目录下创建一个.git目录
入门案例
在这个入门案例中演示如何使用git来做版本管理,主要通过命令行来操作git
-
新建文件夹 learn_git
-
初始化文件夹为代码库:打开终端控制工具,进入文件夹,执行命令
git init
,此时,文件夹中会创建一个名为 ‘.git’ 的子文件夹,不可以修改这个文件夹中的内容,git正是通过它来实现对代码的版本管理。 -
在工作区中编辑文件:工作区就是 learn_git文件夹下除了 .git 目录以外的地方,任意创建一个文件并编辑一些内容
-
将工作区中的文件添加到暂存区:
git add aa.txt
-
把暂存区的文件提交到本地库:
git commit -m '第一次提交'
-
查看提交日志:
git log
这里只演示最基本的使用,更加详细的操作随后学习
代码库
代码库就是存储代码的地方,git的作用,就是管理代码库。
使用git,创建一个代码库,将自己的代码存放到代码库中,就可以对代码进行版本管理了。
代码库的组成
- 工作区:working tree,用户在这个区域中编辑文件
- 版本库:工作区下的 “.git” 目录
- 暂存区:index/stage,位于版本库下的index文件中,编辑后的文件手动添加到暂存区,暂存区也叫做索引
- 仓库:把暂存区域的文件手动提交到仓库,完成编辑。仓库是存储文件和修改记录的地方。git的数据库分为远程仓库和本地仓库。
- 本地仓库:为了方便用户个人使用,在自己的机器上配置的仓库。
- 远程仓库:配有专门的服务器,为了多人共享而建立的。
- master:git自动为用户创建的第一个分支。
- HEAD:执行当前位置的指针
对代码库进行版本管理的大致流程
- 初始化代码库
- 工作区:在工作区编辑文件
- 暂存区:把文件保存到暂存区
- 本地库:把暂存区的文件提交到本地库
- 远程库:把本地库的文件提交到远程库
git的基本操作
要在代码库中使用git命令才有意义
创建代码库
- 初始化代码库:
git init命令
。在一个目录下执行git init命令,这个目录就会被设置为git的仓库,随后所有git命令的执行都要在代码库下执行才有意义。 - .git目录:执行命令后,目录下会出现一个".git"目录,它是git的版本库,所有git需要的数据和资源都存放在这个目录中。代码库本质上是有’.git’子目录的文件夹。
- 编辑配置文件:
git config -e
。使用git默认的编辑器,编辑当前的配置文件
操作代码库
在工作区中编辑文件
工作区,就是和.git子目录平行的其它位置,在工作区中创建几个文件并且编辑一些样例数据。
把工作区中的文件添加到暂存区
使用add命令,把工作区中的文件添加到暂存区:git add <file1 .... | dir1 dir2 | .>
,点代表所有文件
把暂存区中的内容提交到本地库
git commit [-a | -m <message>]
-m <message>
:message是一些备注信息-a
:修改文件后不需要执行git add命令,直接来提交。例如:git commit -am ‘mes’,直接将工作区的文件提交到仓库,但是不建议使用,因为git add命令是为了追踪文件,直接跳过会导致文件缺乏追踪--amend
:修改上一次提交的message
查看提交日志
git log [--pretty=oneline | --oneline] [--stat] [file]
:查看某个文件历史提交记录,或所有的历史提交记录
--oneline
:每个提交记录只显示一行--pretty=oneline
:每个提交记录只显示一行
git reflog
:简略地展示每次提交的信息,而且会额外展示当前指针指向的位置。
从版本库和工作区中删除文件
- 使用rm命令删除工作区的文件,
- 使用
git rm <file>
命令删除本地库的文件, - 执行
git commit -m "msg"
命令,就可以把删除动作提交到本地库,从本地库中删除文件
重命名文件
git mv 旧文件名 新文件名
比较文件的前后差异
git diff [--cached | HEAD] [file]
:默认比较工作区和暂存区之间的文件的差别,字体的颜色,白色表示无变化,红色表示已删除,绿色表示已新增。
- –cached:比较暂存区和本地库之间的文件的差别
- HEAD:比较工作区和本地库之间的文件的差别
git blame <file>
:以列表的形式显示某个文件的修改记录
版本的前进和后退
git reset HEAD~{1,2..} <file>
:使用reset命令来执行版本的前进和后退。reset的含义是重新设置,设置当前HEAD指针到指定的阶段,HEAD指针是git创建的默认指向master分支的指针
- –soft:仅仅移动当前head指针,不会改变工作区和暂存区的内容
- –mixed:默认参数,移动head指针,改变暂存区内容,但不会改变工作区内容
- –hard:当前head指针、工作区、暂存区的内容全部改变
reset命令的三种使用方式:
- 基于索引值来前进或后退版本:
- 第一步:执行命令:git reflog,查看每个版本的局部索引值;
- 第二步:执行命令:git reset --hard <局部索引值>,把当前指针移动到索引值代表的版本,可以前进,也可以后退。
- 使用异或符执行版本后退:git reset --hard HEAD^^^,只可以后退,有几个异或符,表示后退几步
- 使用 ~(波浪线)的形式执行版本后退:git reset --hard HEAD~3,只可以后退,数字是几,表示后退几步
查看工作区和暂存区的当前状态
git status [-s | -b]
- -s:输出简要信息
- -b:展示分支信息
查看帮助信息
git help:在命令行查看简单的帮助信息
git 子命令 --help:会打开一个网页,网页中的内容是当前命令的详细信息
忽略某些在工作区但是不需要进行版本管理的文件
.gitignore文件:在工作中,并不是所有的文件都需要保存到版本库中,例如 target 目录下的文件九可以忽略。在git工作区的根目录下,创建一个特殊的 .gitignore 文件,在这个文件中,每一行指定一个忽略规则。前面的规则会覆盖后面的规则
忽略规则的语法:
#
:注释使用 # 开头- / :以斜杠/开头表示目录
*
:以星号通配多个字符。pattern中有/的时候不匹配/,pattern中没有/的时候*可以匹配/- ** :匹配完整路径,它不管什么斜杠不斜杠的,所有字符通吃
- ? :以问号?通配单个字符
- [] :以方括号[]包含单个字符的匹配列表
- ! :取反,不忽略匹配到的文件
最好在初始化本地库之后就创建 .gitignore 文件并制定忽略规则,在2.x版的git中,如果文件没有被add,并且它符合随后创建的忽略规则,那么它会自动被忽略
git分支
分支:可以理解为代码的不同版本,不同的人操作不同的分支,最后再合并分支。分支可以用于并行推进任务,而且一个任务的失败不会影响到另一个任务,可以增加效率
之前所有的操作都是在同一个分支下进行的,但是在实际开发中,开发者通常是在自己的分支下开发代码,开发完成后,把自己的代码提交到主分支中
master分支:git自带的分支,默认的主分支
新建分支
git branch <分支名>
:如果没有任何参数,表示查看当前所有分支,哪个分支开头带 *,当前就在哪个分支
- -v :表示显示详细信息
- <分支名>:创建分支
- -D <分支名>:删除某个分支
- -M <分支名>:修改当前分支的分支名
- -r:查看远程分支
切换分支
git checkout <分支名>
切换到指定的分支
- -b:以当前分支为基础新建分支
- – file:丢弃工作区的修改,让文件回到最近一次git commit或git add的状态
在当前分支中合并其它分支
git merge <分支名>
,在当前分支中合并指定分支的内容。如果修改有冲突,会合并失败。
git合并分支时遇到的两种情况:fast-forward和Three-way merge。
- fast-forward:例如,从master分支中开辟出某个分支,分支的代码进行了提交,但是master分支没有动,此时,master分支合并开辟出的分支,就是 fast-forward 的情况
- three-way merge:例如,从master分支中开辟出某个分支,分支的代码进行了提交,master分支也提交了自己的修改,此时就有可能产生冲突。当两个分支产生冲突时,git会找到它们共同的祖先,
解决本地合并冲突
- 第一步:如果合并过程中出现冲突,出现冲突的文件中会有一些特殊符号,标明了冲突的位置和内容
- 第二步:编辑文件,删除特殊符号,把文件修改到满意的程度,可以在当前分支修改,也可以切换分支修改,在某个分支的修改不会影响另一个分支的内容
- 第三步:git add 文件名
- 第四步:git commit -m “message” ,此时commit不能带文件名。解决冲突后,会自动完成合并
本地库和远程库的交互
之前所有的操作都是在本地库,在实际开发中,所有开发者从远程库获取代码,开发完成后,需要把代码上传到远程库,然后其他人再从远程库中拉取代码,获取最新的修改
在这里,本地库是使用git维护的本地仓库,远程库是在github上创建的仓库
创建远程库
在github上创建一个仓库,本文件之前的内容中有讲
克隆远程库到本地
git clone <uri>
:它会完整的把远程库下载到本地、创建远程库别名、初始化本地库,在执行命令之前,不用特意创建一个文件夹,这个命令会自动创建文件夹。在github的仓库页面上可以找到代表仓库的uri。
为本地库关联一个远程库
git remote add <name> <url>
,
-
name:通常叫origin
-
url:远程库的url,例如:
https://github.com/wuyaojun108/storage-code.git
,在github页面上可以找到。
如果远程仓库是空的,本地的内容可以直接提交到远程仓库,如果远程仓库有内容,应该先把远程仓库的内容clone下来
本地库的代码推送到远程库
查看本地库管理的远程库地址:git remote -v
执行推送命令:git push [<远程库别名>] [<本地库分支>]
,默认的远程库别名是当前关联的远程库,默认的分支是当前分支
从远程库拉取代码
第一步:git fetch <远程库别名> <本地库分支> ,拉取分支信息
第二步:git pull [<远程仓库名>] [<远程分支名>]:[<本地分支名>]
:默认从当前本地库对应的远程库下同步文件,如果本地分支名是当前分支,可以省略不写。如果pull时报[rejected]错误,表示远程仓库的分支和本地分支出现了冲突,建议修改本地仓库,以和远程仓库保持一致
使用github
在github上创建分支
第一种方式:点击搜索框 - view all branch - create new branch,创建新的分支
第二种方式:也可以在本地创建分支,然后把本地分支提交到github上,提交命令:git push <远程库名称> <本地库分支>,如果远程库没有相同的分支,默认会创建一个
在github上邀请开发者
在仓库界面:settings - collaborators - add people,邀请开发者后,其它开发者也可以为当前项目提交代码
github中的fork和pull request
fork:复制目标仓库到自己的github账户下
pull request:合并其它人的提交,用户跨团队协作
git中的tag
tag是版本库的一个标记,指向某次提交的指针,是静态的、不可移动的。它主要用于标识重要的快照,例如线上发布时使用的版本
注意:tag是针对某次提交的,如果打tag时当前有未提交的代码,未提交的代码不会被包含在tag中,tag没有与分支直接关联。如果tag关联的提交被回退了,tag也会正常存在,但通常不会在开发中打tag,都是上线前为稳定的代码打一个tag。
使用案例:每次发布都使用tag来打发布包,如果要回滚,就使用上一个tag打包,所以tag通常指向一个稳定的代码版本。
操作tag
- 列出所有tag:
git tag
:按字母排序,枚举当前分支上的所有标签。分支不同,tag不同 - 查看tag信息:
git show <tagName>
:显示标签的详情,包括tag所属分支、提交信息、tag创建者和创建时间等。如果tag打在了当前查看的分支,就会查到tag详情;否则,提示异常信息 - 创建tag:
git tag <tagName> [-m <message>]
,例如 git tag v1.0,给当前代码打上v1.0的标签 - 推送tag:
git push <远程库名> <tagName>
- 删除tag:
git tag -d <tagName>
- 切换tag:
git checkout <tagName>
- 拉取远程分支的最新tag:
git fetch
,这个命令用于拉取元数据
tag和分支之间的关系
独立性:tag和分支是两种不同的git引用类型
- 分支:一个可移动的指针,通常用来指向项目的开发历程,允许变化和增长。
- tag:一个固定的指针,用于标记代码历史上一个已知稳定或重要的状态。
用途:
- 分支:用于开发新特性,维护不同的开发线。在分支上可以进行一系列的提交和更改。
- tag:用于对开发过程中的重要点进行标记,例如发布的版本,保持这些状态的不可变性和可追溯性。
操作差异:
- 分支:可以被修改、删除、合并master、从master中创建出一个分支
- tag:tag的创建是为了标记一个固定的点,一旦创建一般不会改变
maven 使用tag打发布包
第一步:将代码切换到某个tag git checkout <tagname>
第二步:打包 mvn clean package
使用某个分支打包也一样,将代码切换到分支,然后执行maven的打包命令
git stash命令
git stash:将本地没有提交的内容进行缓存并从当前分支移除,缓存的数据结构为栈,先进后出。
作用:当用户拉取远程分支的代码,但是当前分支已经作出一些修改时,可以使用git stash命令,隐藏当前分支的修改,然后拉取远程分支,然后再通过git stash命令,弹出刚才的修改,弹出后可能需要合并冲突
git stash命令的使用
- git stash [ save ‘xxx’ ]:加上自己的注解进行缓存
- git stash list:查看缓存列表
- git stash pop:将堆栈中最新的内容pop出来应用到当前分支上,且会删除堆中的记录
- git stash apply:与pop相似,但它不会在堆栈中删除这条缓存
变基操作 rebase
一种将一系列提交从一个分支上摘下来,然后再应用到另一个分支上的机制。这个操作常用于清理提交历史,使得项目的历史变得更加线性和易于理解。变基可以用来整合两个分支的更改,或者将本地分支上的更改重新基于远程分支的最新状态。
变基会改变提交的哈希值,因为它实际上是创建了一个新的提交序列。这意味着变基是一种“破坏性”操作,应该谨慎使用。
变基是一种强大的工具,可以帮助用户维护一个干净有序的项目历史,但在使用时需要考虑到团队协作的影响。在团队中使用变基之前,最好与团队成员沟通,确保大家对变基的操作和影响有共同的理解。
不要在已经推送到远程仓库的分支上进行变基:如果已经将分支推送到了远程仓库,然后对分支进行了变基并再次推送,这可能会给其他协作者带来麻烦,因为他们需要处理非快进更新。
git的使用经验
使用git的正确步骤
先拉取当前分支的最新代码,然后开发代码、提交自己的代码。如果合并到master分支时发现冲突,把master分支的代码合并到当前分支
取消ssl校验
如果配置了ssh,可能会报错,fatal: unable to access 'https://github.com/wuyaojun108/storage-code.git/': OpenSSL SSL_read: Connection was reset, errno 10054。
这是服务器的SSL证书没有经过第三方机构的签署,所以报错。执行命令:git config --global http.sslVerify "false"
,取消ssl校验,可以解决这个问题。
凭据管理器:点击左下角的win图标,输入凭据管理器,然后打开
切换分支时报错
切换分支:git checkout -b <本地分支名> origin/<远程分支名>
报错:fatal:‘xxx’ is not a commit and a branch ‘xxx’ cannot be created from it
原因:因为远程新建的分支没有更新到本地,用checkout命令是从本地仓库找分支的,本地仓库只有在进行网络请求时才会跟远程仓库交互,应该先执行git fatch命令
git的提交规范
在日常开发中,提交代码时,需要说清本次提交的改动点,对于改动点的描述应遵循一定的规范,这可以提升代码历史记录的可读性、可维护性。
commit message的基本格式:
<类型>(<作用域>): <主题>
<空行>
[正文]
<空行>
[脚注]
基本格式中可以分为三个部分:header(第一行,必填)、body(可选)、footer(可选)。
commit message中的每个部分:
1、类型:描述本次修改基本是做了什么,类型的枚举值:
- feat:新增功能,案例,
feat(user):用户模块新增登录功能
- refactor:代码重构,非功能、非缺陷修改
- fix:修复缺陷
- perf:性能优化
- docs:文档变更
- style:代码格式调整,不影响逻辑
- test:测试用例修改
- chore:构建/依赖/工具链变更
- revert:回滚提交
2、作用域:修改的模块、文件等信息
3、主题:一句话描述修改内容
4、正文:详细说明修改内容
5、脚注:本次提交关联的需求文档等内容
案例:
feat(user): 新增用户登录功能
- 用户使用账户密码登录
- 验证码校验
- 登录5次失败后,锁定账号30分钟
需求文档:http://
最佳实践:
- 每次提交应只解决一个问题,避免混合多类变更
- 在主题或正文中引用相关文档
在IDEA中使用git
配置git和github
配置git:file - settings - version control - git,在 path to git executable 对话框中,选择 git.exe 所在的路径
配置github:file - settings - version control - github,主要是配置仓库地址和github账号,点击 + 号,选择 log in with token,点击 generate,登录github,生成 token,复制到IDEA中,点击add account
使用经验
解冲突
如果两个分支对于相同的依赖做了不同的升级:
- 如果依赖是当前项目中的,比如一个模块依赖另一模块,选自己的,合并完成后重新打包。因为同一个项目中,合并代码时会把所有模块的代码都合并,两个分支的改动合并完成后,选择自己的版本号即可
- 如果依赖是第三方的,需要商量
对于代码冲突,如果是不同功能,只是代码恰好在一个类中,merge即可,否则需要和同事商量
回退到某个版本
我遇到的问题是,我修改了两个文件,把它们发到了线上,但是随后发现文件里有错误,造成了问题,希望能够回退到这两个文件之前的版本。
方法1:force push,先在本地回退到想要的版本,然后点击force push,强制提交本地代码,远程分支也会回退,使用force push比较危险,它会修改远程分支的提交历史,需要先和同事打声招呼。(IDEA中,点击push图标旁边的倒三角,就可以看到force push)
方法2:show repository at revision,查看某次提交时的仓库信息,从仓库中找到文件,根据文件中的内容进行回退
第一步:show repository at revison:
第二步:仓库快照信息。在左侧会显示仓库这次提交的快照信息,根据快照中的文件来修改代码,让后重新提交
查看一个分支从哪个分支中被check出来的
git reflog show ${branch-name},查看分支的提交历史
查看某个commit的详情,根据commitId
git show ${commit-id}
diff命令 比较两分支之间的不一致
git diff --name-only ${commitId1} ${commitId2}
查看某个人提交的代码
在提交日志中可以按用户筛选
比较两次提交之间的差异
在git的日志页面,按住ctrl键,选择要比较的两次提交,右击,选择 compare versions
报错记录
pull代码失败 报错 You have divergent branches and need to specify how to reconcile them.
解决方法:在本地执行git命令 git config pull.rebase false
,它用来配置pull操作的默认行为,执行这个命令后,Git会将合并作为拉取远程分支更新时的默认策略,而不是变基(rebase)。
从 Git 2.27 版本开始,Git引入了一个新特性,要求用户明确指定在拉取操作中如何处理已经偏离的分支。这意味着如果用户没有明确配置 pull.rebase 的值,Git 会显示警告信息,并要求用户设置一个默认行为。这样做的目的是为了避免在没有明确意图的情况下不小心覆盖掉分支上的提交历史。
在 Git 的早期版本中,当用户执行 git pull 命令时,如果没有指定特定的策略,Git 默认会尝试快进合并(fast-forward merge)。如果快进合并不适用,Git 会执行一个普通的三方合并,创建一个新的合并提交。