Git
Git是一个分布式版本控制系统(Distributed Version Control System - DVCS)
版本控制系统(Version Control System - VCS)
核心:版本控制,主动提交,中央仓库三个要素
中央式版本控制系统(Centralized VCS)
中央仓库的作用:保存版本历史,同步团队代码
分布式版本控制系统(Distributed VCS/DVCS)
分布式VCS除了中央仓库之外,还有本地仓库。
一.工作模型:
1.首先代码提交到本地仓库;
2.在服务器创建一个中央仓库,把代码从本地仓库推送到服务器的中央仓库;
3.其他同事把中央仓库的内容克隆到本地,拥有了各自的本地仓库;
4.在功能开发中,一个人会把它的每一步改动提交到本地仓库,再从本地仓库推送到中央仓库;
5.另外两个人就可以选择把这些提交同步到自己的机器上,并把它们和自己的本地代码合并。
二.优点:
大多数的操作可以在本地进行,所以速度更快,而且由于无需联网;由于可以提交到本地,所以你可以分步提交代码,把代码提交做得更细,而不是一个提交包含很多代码,难以 review 也难以回溯;
三.缺点:
由于每一个机器都有完整的本地仓库,所以初次获取项目(Git 术语:clone)的时候会比较耗时;本地占用的存储比中央式 VCS 要高。
Git基本使用
下载地址:Git下载
- 在GitHub通过New Repository来新建远程仓库,在Create a new
repository的时候,填写Repository name,Add.gitignore:Android,最后点击Create repository完成创建;创建完成后,点击右边Clone or download,把仓库的clone地址复制到剪贴板; - 从GitHub把中央仓库clone到本地(使用命令:
git clone
)
git clone 复制的地址
注:.git 目录就是你的本地仓库(Local Repository),所有的版本信息都会存在这里,而.git所在的根目录,称为Git的工作目录(Working Directory),它保存了你当前从仓库中checkout的内容。
在项目的目录下输入:git log
查看提交历史 - 代码提交(先用
git add 文件名
把文件添加到暂存区,再用git commit
提交)
在这个过程中,可以使用git status
来随时查看工作目录的状态
image.png
注:每个文件有"changed/unstaged"(已修改),"staged"(已修改并暂存),"commited"(已提交)三种状态,以及一种特殊状态"untracked"(未跟踪)
用add指令来让Git开始跟踪:git add eZhouXingV5-Android
再次输入git status
提交的方式是用commit指令:
git commit
在初始状态下,你是在命令模式,不能编辑这个文件,你需要按一下 "i"(小写)来切换到插入模式,然后就可以输入你的提交信息了;
在输入完成后别按回车,而是要按 ESC 键返回到命令模式,然后连续输入两个大写的 "Z"(用 Shift 键或 Capslock 键都可以),就保存并退出了;
- 提交一次或多次之后,把本地提交push到中央仓库(
git push
)
可以使用git push
来把你的本地提交发布(即上传到中央仓库)
团队工作的基本工作模型
模拟同事的本地仓库:
git clone 地址 文件名
从远程仓库更新内容使用: git pull
流程:同事commit代码到他的本地,并push到GitHub中央仓库;你把GitHub的新提交通过pull指令来取到你的本地;
push冲突:由于 GitHub 的远端仓库上含有本地仓库没有的内容,所以这次 push 被拒绝了;
解决方式:先用 pull 把远端仓库上的新内容取回到本地和本地合并,然后再把合并后的本地仓库向远端仓库推送;
多人合作的基本工作模型:
- 写完所有的 commit 后,不用考虑中央仓库是否有新的提交,直接 push 就好;
- 如果 push 失败,就用 pull 把本地仓库的提交和中央仓库的提交进行合并,然后再 push 一次;
进阶 1:HEAD、master 与 branch
HEAD
当前commit的引用,它指的就是当前工作目录所对应的commit;它具有唯一性,每个仓库中只有一个 HEAD。在每次提交时它都会自动向前移动到最新的 commit 。
branch分支
HEAD 除了直接指向 commit,也可以通过指向某个 branch 来间接指向 commit。当 HEAD 指向一个 branch 时,commit 发生时,HEAD 会带着它所指向的 branch 一起移动。
master 默认branch
主branch/主分支
- 新建的仓库中的第一个 commit 会被 master 自动指向;
- 在 git clone 时,会自动 checkout 出 master。
branch的创建,切换和删除
- 创建branch:
git branch 名称
- 切换branch:
git checkout 名称
可以用git checkout -b 名称
创建后自动切换,用指定的名称创建 branch 后,再直接切换过去; - 删除branch:
git branch -d 名称
HEAD 指向的 branch 不能删除。如果要删除 HEAD 指向的 branch,需要先用 checkout 把 HEAD 指向其他地方。
进阶 2:push 的本质
- push 是把当前的分支上传到远程仓库,并把这个 branch 的路径上的所有 commits 也一并上传;
- push 的时候,如果当前分支是一个本地创建的分支,需要指定远程仓库名和分支名,用
git push origin branch_name
的格式,而不能只用git push
;或者可以通过 git config 修改 push.default 来改变 push 时的行为逻辑。 - push 的时候之后上传当前分支,并不会上传 HEAD;远程仓库的 HEAD 是永远指向默认分支(即 master)的;
进阶 3:merge:合并 commits
- merge含义:从两个 commit「分叉」的位置起,把目标 commit 的内容应用到当前 commit(HEAD 所指向的 commit),并生成一个新的 commit;执行
git merge branch1
- merge的使用场景:
单独开发的branch用完了以后,合并回原先的branch;
git pull的内部自动操作
pull的内部操作:pull的实际操作其实是把远端仓库的内容用fetch取下来之后,用merge来合并;
merge在合并的时候,是有一定的自动合并能力的,如果一个分支改了A文件,另一个分支改了B文件,那么合并后就是既改A也改B;如果两个分支都改了同一个文件,但一个改的是第 1 行,另一个改的是第 2 行,那么合并后就是第 1 行和第 2 行都改,也是自动完成。
-
merge的三种特殊情况
3.1 原因:当前分支和目标分支修改了同一部分内容,Git无法确定应该怎样合并;
应对方法:解决冲突后手动commit;
image.png
Git 添加了三行符号 <<< === >>>
3.2 HEAD领先于目标commit: Git什么也不做,空操作;
3.3 HEAD落后于目标commit:fast-forward;
进阶 4:Feature Branching:最流行的工作流
简介:
- 任何新的功能(feature)或 bug 修复全都新建一个 branch 来写;
- branch 写完后,合并到 master,然后删掉这个 branch;
git checkout -b books;//创建一个新的branch叫做books
git push origin books;//把代码push到中央仓库
git pull;//同事从中央仓库拉下代码
git checkout books;//切换到分支books
// 合并到master
git checkout master;
git pull;//merge之前pull一下,让master更新到和远程仓库同步
git merge books;//合并books分支
// 合并后的结果push到了中央仓库,并删掉了books这个branch
git push;
git branch -d books;
git push origin -d books// 用-d参数把远程仓库的branch也删了
进阶 5:关于 add
add把改动的内容放进暂存区
1. add后面加个点'.':全部暂存
git add .
直接把工作目录下的所有改动全部放进暂存区;
2. add添加的是文件改动,而不是文件名
进阶 6:看看我都改了什么
git log
可以查看历史记录
1. 查看历史中的多个commit: log
查看详细改动:git log -p
;
查看大致改动: git log --stat
2. 查看具体某个commit: show
要看最新 commit ,直接输入 git show
;要看指定 commit ,输入 git show commit的引用或SHA-1
;
如果还要指定文件,在 git show 的最后加上文件名
3. 查看未提交的内容:diff
查看暂存区和上一条 commit 的区别:git diff --staged(或 --cached)
查看工作目录和暂存区的区别:git diff 不加选项参数
查看工作目录和上一条 commit 的区别:git diff HEAD
高级 1:不喜欢 merge 的分叉?用 rebase 吧
rebase——在新位置重新提交,重写设置基础点,改变commit序列的基础点
使用方式:git rebase 目标基础点
git checkout branch1
git rebase master
// 在rebase之后,记得切回master再merge一下,把master移动最新的commit:
git checkout master
git merge branch1
高级 2:刚刚提交的代码,发现写错了怎么办?
用 commit --amend 可以修复当前提交的错误: git commit --amend
,commit --amend 并不是直接修改原 commit 的内容,而是生成一条新的 commit。
git add 笑声.txt
git commit --amend
高级 3:写错的不是最新的提交,而是倒数第二个?
commit --amend 可以修复最新 commit 的错误,但如果是倒数第二个 commit 写错了,怎么办?
rebase -i:交互式 rebase
开启交互式 rebase 过程: git rebase -i HEAD^^
把当前 commit ( HEAD 所指向的 commit) rebase 到 HEAD 之前 2 个的 commit 上;
交互式 rebase 最常用的场景是修改写错的 commit
- 使用方式是
git rebase -i 目标commit;
- 在编辑界面中指定需要操作的 commits 以及操作类型;
- 操作完成之后用
git rebase --continue
来继续 rebase 过程。
高级 4:比错还错,想直接丢弃刚写的提交?
reset --hard 丢弃最新的提交
git reset --hard HEAD^
:HEAD^ 表示你要恢复到哪个 commit。因为你要撤销最新的一个 commit,所以你需要恢复到它的父 commit ,也就是 HEAD^。那么在这行之后,你的最新一条就被撤销了
高级 5:想丢弃的也不是最新的提交?
用交互式 rebase 撤销提交
交互式 rebase 可以用来修改某些旧的 commits。其实除了修改提交,它还可以用于撤销提交;
你想撤销倒数第二条 commit,那么可以使用 rebase -i:git rebase -i HEAD^^
用 rebase --onto 撤销提交
在 rebase 命令中直接剔除想撤销的 commits
高级 6:代码已经 push 上去了才发现写错?
1. 出错的内容在你自己的 branch
如果你在本地对已有的commit做了修改,这时你再push就会失败,因为中央仓库包含本地没有的commits,这时选择强行push:
git push origin branch1 -f
:忽略冲突,强制push
2. 出错的内容已经合并到 master
增加一个新的提交,把之前的提交的内容抹掉;
不要强制 push,而要用 revert 把写错的 commit 撤销;git revert HEAD^
高级 7:reset 的本质——不止可以撤销提交
reset 的本质:移动 HEAD 以及它所指向的 branch
reset --hard HEAD^
它把 HEAD 和它所指向的 branch 一起移动到了当前 commit 的父 commit 上,从而起到了「撤销」的效果;
把 HEAD 和 branch 移动到其他的任何地方:git reset --hard branch2
reset --hard:重置工作目录
重置位置的同时,清空工作目录的所有改动;
reset --soft:保留工作目录
r重置位置的同时,保留工作目录和暂存区的内容,并把重置 HEAD 的位置所导致的新的文件差异放进暂存区;
reset 不加参数:保留工作目录,并清空暂存区
--mixed(默认):重置位置的同时,保留工作目录的内容,并清空暂存区。
高级 8:checkout 的本质
git checkout branch名
: checkout 的本质是签出指定的 commit,所以你不止可以切换 branch,也可以直接指定 commit 作为参数,来把 HEAD 移动到指定的 commit。
checkout 和 reset 的不同
reset 在移动 HEAD 时会带着它所指向的 branch 一起移动,而 checkout 不会。当你用 checkout 指向其他地方的时候,HEAD 和 它所指向的 branch 就自动脱离了。
高级 9:紧急情况:「立即给我打个包,现在马上!」
stash:临时存放工作目录的改动
在 Git 中,stash 指令可以帮你把工作目录的内容全部放在你本地的一个独立的地方,它不会被提交,也不会被删除,你把东西放起来之后就可以去做你的临时工作了,做完以后再来取走,就可以继续之前手头的事了。git stash
打完包切回你的分支: git stash pop
高级 10:branch 删过了才想起来有用?
branch 用完就删是好习惯
reflog :引用的 log
查看一下 HEAD 的移动历史:git reflog
查看其他引用的 reflog
reflog 默认查看 HEAD 的移动历史,除此之外,也可以手动加上名称来查看其他引用的移动历史:git reflog master
额外说点:.gitignore——排除不想被管理的文件和目录
.gitignore记录了所有你希望被 Git 忽略的目录和文件。
Git原理详解及实用指南