参考资料:廖雪峰的博客 https://www.liaoxuefeng.com/
在上文中,我们已经在linux系统通过源码成功编译安装了 git-2.7.0版本,现在我们继续学习如何使用 git 。
1、添加用户和邮箱
因为git是分布式版本控制系统,所以需要我们填写“用户名”和“邮箱”作为 本地分支库 在 git分布式版本库 中的唯一标识。
git config --global user.name xxxx # 设置本机的git用户名
git config --global user.email xxxx #设置本机的git邮箱
git config user.name #查看用户名
git config user.email #查看邮箱
git config --global user.name xxxx #修改用户名
git config --global user.email xxxx@xxx.com #修改邮箱
注意: git config 命令使用 --global 参数,表示这台机器上的所有 git 仓库都会使用这个配置。当然也可以对某个仓库指定不同的用户名和邮箱。
2、创建版本库
mkdir git_repository #选择一个合适的地方创建一个仓库目录
cd git_repository
git init #仓库初始化,会在当前的仓库文件夹下生成一个隐藏的 .git文件,这样一个仓库就创建好了
3、添加文件到版本库
所有的版本控制系统,其实只能跟踪文本文件的改动,比如TXT文件、网页、程序代码等。不可识别图片、word文档等二进制文件的具体改动。
mkdir document #在 git_repository 仓库创建存放文档的子目录,并向目录(工作区)中上传了 readme.txt 文本文件
git add document/readme.txt #用 git add 命令告诉 git ,把文件从工作区添加到版本库(也叫暂存区,就是 .git 文件夹)
git commit -m "wrote a readme file" #用 git commit 命令告诉 git ,把文件提暂存区交到本地版本库,-m 参数后面是本次版本提交的说明和备注,以便追溯
注:
1) git add <file> ,也可以添加多个文件,git commit 会将添加的文件一次性提交。
2) 对于已存在的文件,继续 git add <file> ,可以将该文件 commit 到版本库
3) git add 、git commit -a 会把本地修改内容全部提交。
操作流程小结:git init 初始化仓库,git add <file> 添加文件到暂存区,git commit 将暂存区stage 的内容提交到分支版本库。
4) git add -a #将提交所有变化
git add -u #提交修改和删除文件,不包括新增文件
git add . #提交修改和新增文件,不包括删除文件
4、版本追溯、对比、还原
我们修改一下 readme.txt 文件中的内容,然后查看一下仓库状态。
git status #让我们时刻掌握仓库当前的状态,提示 readme.txt 修改后还没有 commit,新文件 test.txt 需要 git add 添加到仓库。
注:On branch master # 默认使用的是 master 主分支
git commit -a #提交修改内容,弹出如下窗口,需要你把要提交的内容前面的 # 号注释去掉,然后保存并退出
显示提交已成功:
git diff <file> #对比 工作区 和 版本库 的不同之处,绿色内容相对版本库是新增内容,对比之后再 git commit -a 就放心多了
git checkout -- <file> #直接检出最新版本的文件,覆盖当前文件。适用于文件被改乱了改错了,直接还原。
小结:
1) git status 可以查看工作区状态,提示你有哪些文件是新增或被修改过的。
2) git diff <file> 可以查看文件的改动内容
3) git checkout -- <file> 干掉该文件所有的修改,还原到最新版本,一定要加 “--”,否则就是切换分支的命令了
5、查询历史 及 版本回退
git log #由近及远查看历史版本
git log --pretty=oneline #防止眼花缭乱,单行输出历史版本
注:黄色字体是版本id,不使用数字号作为版本号,是因为git是分布式版本管理,防止多人在同一版本库里工作的版本号冲突
git reset --hard HEAD^ #回退版本:HEAD^ 上个版本; HEAD^^ 上上个版本; HEAD~10 往上10个版本
回退前有4个版本,然后执行回退:
最近的一个版本已被回退,回退后的结果,且回退后根据 git diff 来看回退后不需要再 commit:
cat readme.txt #看内容是否已被回退:刚才提交的第6点已经被回退了
问1:刚回退之后,我又想再还原到 id 为 f2dad... 的那个最新版本,该怎么办呢?
git reset --hard <版本号id> #指定 id 回退到某个版本
刚刚回退了的版本又被还原回来了~
问2:昨天回退了最新版本,今天后悔了,但是我已经不记得最新版本的 id 了,我该怎么还原回去呢?
git reflog readme.txt #这个命令记录了你每一次执行的命令
从图中可以看出,git reflog 记录了最后一次 commit 的版本 id,我们就找到了提交过的最新版的文档的 id,以此还原。
小结:
1) git log 查看提交历史,以便确定回退版本
2) git reflog 查看命令历史 和 版本号。以便知道对哪个版本做过什么
6、工作区与版本库的概念
工作区:存放文档、代码的地方(git_repository 文件夹)
版本库:工作区里面有个隐藏的 .git 文件夹,这个文件夹就是版本库/暂存区
工作原理:git commit <file> 命令只会提交已经 git add 过的内容,即 commit 提交的是版本库 stage 区的内容,而不会提交工作区的内容。做个实验理解一下。
1) 对 readme.txt 文件修改第一次,执行 git add
2) 对 readme.txt 文件修改第二次,不 git add
3) git status
可发现:绿色字体表示第一次提交的修改可以被commit,红色字体表示新修改没有被 add 到 stage 区,不能 commit
4) git commit -m "备注内容"
# 测的时候一定要加 -m 参数,git commit、git commit -a 都把我没 add 的内容也给 commit 了
5) git diff readme.txt #对比工作区版本和版本库版本
6) 9是修改后被 add 到 stage 区的,10 只做了修改没有被 git add,commit 只提交了9,显示 10 和版本库是不一样的
结论:git 优秀的地方在于跟踪和管理修改而非文件,这些修改需要 git add 后才会存放在 stage 区。
删除文件
方法1:从工作区删除文件,然后 git commit <file>
方法2:git rm <file> #从版本库到工作区全都删除
7、提交远程仓库(介绍先有本地库,后有远程库,该如何关联)
我们使用 GitHub 作为远程仓库,注册 GitHub 用户 && 登录,创建一个你的仓库
1) 在你的仓库,有默认的 master 主分支,可以从 master 继续创建副分支
2) 也可以把本地 git 仓库关联到 GitHub 的远程仓库下面
git remote add origin https://github.com/Jingjp/repository01.git
# 给本地仓库关联一个远程仓库,地址 https://github.com/Jingjp/repository01.git,标签为 origin
git push -u origin master
#把本地 master 分支内容推送到远程仓库 origin 分支下。第一次推送时,我们使用了 -u 参数,git 会把本地 master 分支和远程的 origin分支关联,以后推送或者拉取远程仓库内容就可以简化命令了。
git push origin master #把本地 master 分支上提交了的内容推送到远程仓库 origin 下
git push origin : master #如果本地分支没写,就会提交一个空分支,相当于删除远程分支
小结:
git remote add origin https://github.com/Jingjp/repository01.git #给远程主机添加标签
git push -u origin master #提交当前分支到 origin 的 master
git push origin master
git branch --set-upstream-to origin/<branch-name> <branch-name> #将"本地"和"远程"关联
8、从远程仓库克隆
从零开发的情况下,最好是先创建远程库,然后从远程库克隆到本地
首先登录 GitHub,创建一个新的仓库 repository02,创建的时候勾选初始化一个 README.md 文件
远程库创建好了,我们在本地 linux 找好存放仓库的位置,就可以使用 git clone 命令克隆远程库了
git clone https://github.com/Jingjp/repository02.git
git clone -b dev https://github.com/Jingjp/repository02.git #从指定远程分支克隆
(git支持多种协议,如果不能使用https,就用 git clone git@github.com:Jingjp/repository02.git)
非常完美,repository02 中有 .git 、README.md 两个文件
8.1 如果想指定标签如何克隆
9、远程代码推拉
向远程仓库推送 - push
git push命令用于将本地分支的更新,推送到远程主机。它的格式与git pull命令相仿。
$ git push <远程主机名> <本地分支名>:<远程分支名>
注意,分支推送顺序的写法是<来源地>:<目的地>,所以git pull是<远程分支>:<本地分支>,而git push是<本地分支>:<远程分支>。
如果省略远程分支名,则表示将本地分支推送与之存在”追踪关系”的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。
$ git push origin master
上面命令表示,将本地的master分支推送到origin主机的master分支。如果后者不存在,则会被新建。
删除远程分支:
如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。
$ git push origin :master
# 等同于
$ git push origin -d master (这个分支需先在本地checkout,才可以删除远程)
上面命令表示删除origin主机的master分支。
如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略。
$ git push origin
上面命令表示,将当前分支推送到origin主机的对应分支。
如果当前分支只有一个追踪分支,那么主机名都可以省略。
$ git push
如果当前分支与多个主机存在追踪关系,则可以使用 -u 选项指定一个默认主机,这样后面就可以不加任何参数使用git push。
$ git push -u origin master
上面命令将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了。
不带任何参数的git push,默认只推送当前分支,这叫做simple方式。此外,还有一种matching方式,会推送所有有对应的远程分支的本地分支。Git 2.0版本之前,默认采用matching方法,现在改为默认采用simple方式。如果要修改这个设置,可以采用git config命令。
$ git config --global push.default matching
# 或者
$ git config --global push.default simple
还有一种情况,就是不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这时需要使用–all选项。
$ git push --all origin
上面命令表示,将所有本地分支都推送到origin主机。
如果远程主机的版本比本地版本更新,推送时Git会报错,要求先在本地做git pull合并差异,然后再推送到远程主机。这时,如果你一定要推送,可以使用 –force 选项。(连推送带合并代码)
$ git push --force origin
上面命令使用 –force 选项,结果导致在远程主机产生一个”非直进式”的合并(non-fast-forward merge)。除非你很确定要这样做,否则应该尽量避免使用–force选项。
最后,git push不会推送标签(tag),除非使用–tags选项。
$ git push origin --tags
有时候当远程xxx分支被删掉了后,用git branch -a 你还可以看到本地还有remote/origin/xxx这个分支,那么你可以使用git fetch -p 这个命令可以帮你同步最新的远程分支,并删掉本地被删了的远程分支
从远程仓库拉取 - pull
取回远程主机某个分支的更新,与本地的指定分支合并
git pull origin <远程分支>:<本地分支>
1) 如果与当前分支合并,可省略本地分支名
git pull origin
<远程分支>
相当于:git fetch origin
<远程分支>
git merge origin/<远程分支>
2) 如果当前分支与远程分支存在追踪关系
git pull origin
3. 如果当前分支只有一个追踪关系
git pull
4) 手动建立追踪关系
git branch --set-upstream master origin/next
5. 清理远程已删除本地还存在的分支
git fetch --prune origin
或者 git fetch -p
或者 git pull -p
如果没有clone,先clone
$ git clone ...
查看远程分支
$ git branch -a
切换到远程分支
$ git checkout origin/master
切换到 origin/Release分支,并在本地新建分支 myRelease
$ git checkout -b myRelease origin/Release
Branch myRelease set up to track remote branch Release from origin.
Switched to a new branch 'myRelease'
10、分支的创建与合并
多人开发的情况下,每个人的代码库就是一个分支,单独开发互不影响,但最终会在某个时刻把分支代码合并,盗个图给大家理解下:
分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。
现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。
Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。
分支的创建与合并
cd git_repository #(之前创建的本地版本库),对库的 master 主分支创建副分支 dev
git branch dev # 在“当前分支”创建新分支”dev
git checkout dev # 切换到分支 dev
git branch #查看所有分支,dev 分支前面有个 *,表示当前正在使用的分支是 dev
我们来做下测试,在 dev 分支做一些修改,然后提交,最后将分支合并到 master。
1) 首先确认现在是 dev 分支,修改 readme.txt,并添加提交。
2) git checkout master # 切换回 master 分支
git branch # 确认当前分支
3) git merge dev # 合并“指定分支(dev)”到“当前分支(master)”,发现内容已经合并到 master
4) git branch -d dev # 删除 dev,只留 master
git branch -D dev # 强行删除分支(如未合并过的分支)
因为创建、合并和删除分支非常快,所以Git鼓励你使用分支完成某个任务,合并后再删掉分支,这和直接在master
分支上工作效果是一样的,但过程更安全。
小结:
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>
创建+切换分支:git checkout -b <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
11、解决分支冲突
开始测试:
1) git checkout -b feature # 创建并切换到新分支 feature
修改 readme.txt 中的内容,并提交
2) git checkout master #切换回 master
继续对 readme.txt 做修改,并提交
3) git merge feature # master 分支合并 feature
我们发现合并失败,查看 readme.txt 中的内容
4) 删掉 <<< === >>>,修改冲突的内容。
重新用master 分支提交,再合并 feature 分支。
5) 删除分支
结论:
1) 把冲突内容改成了新的内容,仍能合并成功。git 默认当人工处理过以后,即便有冲突也以处理过的为准,不再在同一个地方报冲突了。
2) master 合并 feature 分支成功,冲突内容也变成了手动处理过的内容,但返回 feature 分支看 readme.txt,内容还是原来的内容,没有被 master 同步
小结
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
用 git log --graph
命令可以看到分支合并图
12、分支管理策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
1) master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
2) 干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在 master
分支发布1.0版本;
3) 你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
13. 标签(tag)的管理
首先明确:
- 标签没有分支的概念。当你在master分支打个1.0的标签后,这个标签在dev分支也能看到。且在dev也不能再重复使用这个标签。
- 指定标签克隆的代码,适合用来打包构建,不适合用来开发
- 当标签从v1.0 升级到 v2.0,指定v1.0克隆的代码是没有分支的,如果切换到具体分支如master。则master分支会包含克隆前远程master分支的最新内容。
指定标签去拉代码,更多的是用来使用这一版本的内容来运行或者构建打包。应用场景主要包括使用github开源的内容。
$ git clone -b 1.4.1 http://code.aliyun.com/test/springcloud.git
打上1.0的标签
$ git tag 1.0
查看标签列表
$ git tag
查看标签下内容及备注
$ git show 1.0
默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?
方法是找到历史提交的commit id,然后打上就可以了:
$ git log --pretty=oneline --abbrev-commit
$ git tag 1.1 afdecb0
还可以创建带有说明的标签,用-a
指定标签名,-m
指定说明文字:
$ git tag -a 1.4.1 -m "add swagger2" 1094adb
删除标签,多个标签可一起删除:
$ git tag -d 1.1 1.2 1.3
如果要推送某个标签到远程,使用命令 git push origin <tagname>
:
$ git push origin v1.0
或者,一次性推送全部尚未推送到远程的本地标签:
$ git push origin --tags
如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
$ git tag -d v0.9
然后,从远程删除。删除命令也是push,但是格式如下(和删除远程分支意思相同,即推送一个空的标签到远程):
$ git push origin :refs/tags/v0.9
14、git stash 储藏、恢复
应用场景:1、代码写到一半,突然一个bug需要紧急修复,或者有另一个新的任务需要优先完成
2、pull 拉取代码时,和本地内容冲突,就需要先把本地修改储藏起来再 pull。
git stash # 把当前工作“储藏”起来。
git stash save "注释内容" # 储藏工作,并添加注释
git stash list # 查看储藏的工作列表
git stash apply stash@{0} # 将 stash@{0} 的工作恢复,不删除储藏列表
git stash pop stash@{0} # 将 stash@{0} 的工作恢复,删除储藏列表
git stash drop stash@{0} # 将 stash@{0} 从储藏列表删除
git stash clear # 清空储藏列表
git stash branch <branchname> stash@{0} # 创建一个新分支,并恢复储藏内容
15、多人协作开发
16、搭建 git 服务器