版本控制

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下载

  1. 在GitHub通过New Repository来新建远程仓库,在Create a new
    repository的时候,填写Repository name,Add.gitignore:Android,最后点击Create repository完成创建;创建完成后,点击右边Clone or download,把仓库的clone地址复制到剪贴板;
  2. 从GitHub把中央仓库clone到本地(使用命令:git clone)
    git clone 复制的地址
    注:.git 目录就是你的本地仓库(Local Repository),所有的版本信息都会存在这里,而.git所在的根目录,称为Git的工作目录(Working Directory),它保存了你当前从仓库中checkout的内容。
    在项目的目录下输入:git log 查看提交历史
  3. 代码提交(先用git add 文件名把文件添加到暂存区,再用git commit提交)
    在这个过程中,可以使用git status来随时查看工作目录的状态
    image.png

    注:每个文件有"changed/unstaged"(已修改),"staged"(已修改并暂存),"commited"(已提交)三种状态,以及一种特殊状态"untracked"(未跟踪)

用add指令来让Git开始跟踪:git add eZhouXingV5-Android
再次输入git status

image.png

提交的方式是用commit指令:git commit
在初始状态下,你是在命令模式,不能编辑这个文件,你需要按一下 "i"(小写)来切换到插入模式,然后就可以输入你的提交信息了;
在输入完成后别按回车,而是要按 ESC 键返回到命令模式,然后连续输入两个大写的 "Z"(用 Shift 键或 Capslock 键都可以),就保存并退出了;

  1. 提交一次或多次之后,把本地提交push到中央仓库(git push)
    可以使用git push来把你的本地提交发布(即上传到中央仓库)

团队工作的基本工作模型

模拟同事的本地仓库:
git clone 地址 文件名
从远程仓库更新内容使用: git pull
流程:同事commit代码到他的本地,并push到GitHub中央仓库;你把GitHub的新提交通过pull指令来取到你的本地;

push冲突:由于 GitHub 的远端仓库上含有本地仓库没有的内容,所以这次 push 被拒绝了;
解决方式:先用 pull 把远端仓库上的新内容取回到本地和本地合并,然后再把合并后的本地仓库向远端仓库推送;
多人合作的基本工作模型:

  1. 写完所有的 commit 后,不用考虑中央仓库是否有新的提交,直接 push 就好;
  2. 如果 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/主分支

  1. 新建的仓库中的第一个 commit 会被 master 自动指向;
  2. 在 git clone 时,会自动 checkout 出 master。
branch的创建,切换和删除
  1. 创建branch:git branch 名称
  2. 切换branch: git checkout 名称
    可以用git checkout -b 名称创建后自动切换,用指定的名称创建 branch 后,再直接切换过去;
  3. 删除branch: git branch -d 名称
    HEAD 指向的 branch 不能删除。如果要删除 HEAD 指向的 branch,需要先用 checkout 把 HEAD 指向其他地方。

进阶 2:push 的本质

  1. push 是把当前的分支上传到远程仓库,并把这个 branch 的路径上的所有 commits 也一并上传;
  2. push 的时候,如果当前分支是一个本地创建的分支,需要指定远程仓库名和分支名,用git push origin branch_name 的格式,而不能只用 git push;或者可以通过 git config 修改 push.default 来改变 push 时的行为逻辑。
  3. push 的时候之后上传当前分支,并不会上传 HEAD;远程仓库的 HEAD 是永远指向默认分支(即 master)的;

进阶 3:merge:合并 commits

  1. merge含义:从两个 commit「分叉」的位置起,把目标 commit 的内容应用到当前 commit(HEAD 所指向的 commit),并生成一个新的 commit;执行git merge branch1
  2. merge的使用场景:
    单独开发的branch用完了以后,合并回原先的branch;
    git pull的内部自动操作

pull的内部操作:pull的实际操作其实是把远端仓库的内容用fetch取下来之后,用merge来合并;
merge在合并的时候,是有一定的自动合并能力的,如果一个分支改了A文件,另一个分支改了B文件,那么合并后就是既改A也改B;如果两个分支都改了同一个文件,但一个改的是第 1 行,另一个改的是第 2 行,那么合并后就是第 1 行和第 2 行都改,也是自动完成。

  1. merge的三种特殊情况
    3.1 原因:当前分支和目标分支修改了同一部分内容,Git无法确定应该怎样合并;
    应对方法:解决冲突后手动commit;


    image.png

    Git 添加了三行符号 <<< === >>>
    3.2 HEAD领先于目标commit: Git什么也不做,空操作;
    3.3 HEAD落后于目标commit:fast-forward;

进阶 4:Feature Branching:最流行的工作流

简介:

  1. 任何新的功能(feature)或 bug 修复全都新建一个 branch 来写;
  2. 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

  1. 使用方式是 git rebase -i 目标commit;
  2. 在编辑界面中指定需要操作的 commits 以及操作类型;
  3. 操作完成之后用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 忽略的目录和文件。


image.png

Git原理详解及实用指南

你可能感兴趣的:(版本控制)