Git安装
安装完成后,用以下命令将用户信息全局初始化。
1 | $ git config --global user.name "Your Name" |
因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址。你也许会担心,如果有人故意冒充别人怎么办?这个不必担心,首先我们相信大家都是善良无知的群众,其次,真的有冒充的也是有办法可查的。
**注意: **git config命令的–global参数,用了这个参数,表示你这台机器上所有的Git仓库都会使用这个配置,当然也可以对某个仓库指定不同的用户名和Email地址。
创建版本库并初始化
1 | $ mkdir learngit |
以上三行命令分别为创建目录、进入目录以及查看当前目录位置。
注意: 路径中最好不要有中文
1 | $ git init |
初始化,会在此目录下建立一个隐藏的.git文件夹,用来跟踪管理版本库。
言归正传,现在我们编写一个readme.txt文件,内容如下:
1 | $ git add readme.txt |
添加文件,可连续多次添加。
1 | $ git commit -m "wrote a readme file" |
提交文件,把添加的所有文件一次提交,-m后面是提交信息,最好写有意义的描述。然后可以看到修改信息。
时光机穿梭
查看状态
先将原文件的第7行加入单词distributed ,然后在第8行后面添加一行Very Good!
显示当前库的状态,会提示变动过哪些文件。
1 | $ git status |
版本对比
查看和上一版本的具体变动内容
1 | $ git diff test.txt |
显示内容如下:
1 | diff --git a/test.txt b/test.txt |
详解:
- diff –git a/test.txt b/test.txt ——对比两个文件,其中a改动前,b是改动后,以git的diff格式显示;
- index 629d9c8..3d98a7f 100644 ——两个版本的git哈希值,index区域(add之后)的 629d9c8 对象和工作区域的 3d98a7f 对象, 100表示普通文件,644表示权限控制;
- — a/test.txt +++ b/test.txt ——减号表示变动前,加号表示变动后;
- @@ -4,8 +4,9 @@ test line3. ——@@表示文件变动描述合并显示的开始和结束,一般在变动前后多显示3行,其中-+表示变动前后,逗号前是起始行位置,逗号后为从起始行往后几行。合起来变动前后都是从第4行开始,变动前文件往后数8行对应变动后文件往后数9行。
- 变动内容 ——+表示增加了这一行,-表示删除了这一行,没符号表示此行没有变动。
版本回退
1 | $ git log |
用来查看最近三次提交的记录
1 | $ git log --pretty=oneline |
合并每条记录到一行
1 | $ git reset --hard HEAD^ |
向前回退版本,其中HEAD后面跟几个^就是往回退几个版本,如果回退100个版本,可以写成 HEAD~100 。
1 | $ git reset --hard 07e0 |
向后恢复版本,首先要查找到对应版本的哈希id前4位,如果提交窗口找不到,可以使用以下命令
1 | $ git reflog |
这个命令记录了每一次版本相关的操作。git回退的速度非常快,因为在git内部有一个指向当前版本的HEAD指针,回退到某个版本,实际上是git把指针移动指向某个版本
工作区和暂存区
- 工作区(Working Directory):.git所在的目录下,除了.git之外的其他文件都是在工作区内
- 版本库(Repository):.git目录内所存的记录,有暂存区和Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
- stage(或者叫index)的暂存区:用add命令放进来文件的位置
- 如果文件在工作区被编辑,对应的status状态就是 Changes not staged for commit
- 如果工作区新增文件,则对应的status状态就是 Untracked files
- 如果文件被add后,对应的status状态就是 Changes to be committed
- 多次add后的文件都放在暂存区,最后一次性全部提交。提交后的status状态就是 nothing to commit, working tree clean这时候工作区就是干净的,暂存区就没有任何内容了。
修改
管理修改
如果一个文件,修改一次后,add,再修改一次后直接commit,然后status则显示还有一次修改没有被提交,因为提交只对暂存区生效。所以要么每改动一次后都add,最后一次性提交;要么add一次就提交一次。
比较工作区与暂存区
git diff 不加参数即默认比较工作区与暂存区
比较暂存区与最新本地版本库(本地库中最近一次commit的内容)
git diff –cached [
比较工作区与最新本地版本库
git diff HEAD [
撤销修改
如果在工作区修改了文件后的status,会提示,下一步可以add到暂存区,或者从暂存区恢复修改:
1 | Changes not staged for commit: |
想要撤销,就用第3行的命令
$ git checkout – test.txt
如果已经add到暂存区了,这时想要撤销操作,这时可以从status中的提示——从HEAD中恢复修改。
$ git reset HEAD
但这时候暂存区的修改撤销了,工作区还是修改后的内容,此时再使用上面提交的 $ git checkout – test.txt 来撤销工作区修改,世界终于变得清净了!
删除文件
1 | $ rm test2.txt |
此命令可以从工作区删掉文件。如果要从版本库中删除,则add后提交即可,如果是误删了,则通过
1 | $ git checkout -- test2.txt |
从版本库里恢复。
如果已经将删除提交,则像前面一样先恢复版本库,然后在checkout出要恢复的文件。
分支管理
##概述
假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。这种情况下需要分支来管理。自己在创建的新分支上进行开发,完成后一次性提交合并即可。
Git对于分支的创建、切换和删除都能非常快的实现,而SVN就很慢。
创建与合并分支
git单分支的结构是这样的,master是指向最新提交的指针,HEAD是指向master的指针,每做一次提交,指针就向前移动一步:
现在增加一个dev分支并切换到这个分支:
1 | $ git -b checkout dev |
这个代码可以写成两步,本别是创建新分支 $ git branch dev ,切换到目标分支 $ git checkout dev ,之后变成这样:
这时可以用命令查看分支
1 | $ git branch |
其中带星号的是当前所在分支。然后再新分支上做一些更改,再add并提交,这是结构变成了这样:
切换回master($ git checkout master )分支后,发现刚才所做的改动不见了,是因为改动在dev分支上。
这时使用合并命令:
1 | $ git merge dev |
即把目标分支合并到当前分支上。完成后提示 Fast-forward ,说明系统用了快进模式进行合并,此时的结构为:
master分支上也成了最新版。这时不需要dev分支了,可以删除:
1 | $ git branch -d dev |
这时再查看分支,已经没有dev了。
解决冲突
当两个分支上对同一个文件有修改并分别有提交,最后Git无法自动合并,就会产生冲突。
1 | $ git merge 'feature2' |
如果你不服气再执行一次合并,就会看到:
1 | $ git merge feature2 |
这时候可以通过 git status 查看冲突信息,找到描述中冲突的文件:
1 | <<<<<<< HEAD |
其中 <<<<<<< HEAD 和 ======= 之间是当前分支的最新版, ======= 和 >>>>>>> f4 之间是目标分支内容,手动修改后删掉这些符号,然后提交,结构如图:
可以用以下代码看到图形化流程:
$ git log –graph –pretty=oneline –abbrev-commit
其中, –graph 是图形化, –pretty=oneline 是一行显示, –abbrev-commit 是只显示每次提交id的前几位,显示效果如下:
分支管理策略
默认情况下,如果情况允许,Git会自动用快进模式合并分支,但这样合并后不会留下分支存在过的痕迹。删除分支后就会丢失相应信息。如果不想这样做,则在合并时加上参数 –no-ff ,Git则会生成一个提交,所以同时再加上一个提交信息(如果不加则会进入vim模式让编辑提交信息),代码如下:
$ git merge –no-ff -m “merge with no-ff” dev
这样合并后还能看到对应的分支信息,如图,
实际开发中的分支策略:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
平时大家在dev分支上干活,需要发布时合并到master分支即可。
Bug分支
假设正在dev上开发,突然接到修复master上一个bug,就可以用如下命令把现场保存起来(这个命令比其他命令要执行的慢):
$ git stash
这时工作区就是干净的,刚才的改动不见了。然后把分支切换到master,并在此基础上新建并切换到bug分支issue-101,在这里修复bug。修复完成后回到master分支,进行非快速合并后删除bug分支,再切换回dev分支,可以通过加list参数看到 stash 的列表:
$ git stash list
stash@{0}: WIP on d3: 820373a 合并d2, Merge branch ‘d2’
通过如下命令恢复现场:
$ git stash apply
这时stash区的内容还存在,可以用list查看,如果要清理掉,就用
$ git stash drop
这时stash区什么都没了。如果将工作区内容多次保存到stash,则可以加 stash@{0} 这样的编号来指定恢复哪个(可用list参数查看编号)。
$ git stash apply stash@{0}
也可以用如下命令弹出最后一次保存的工作区内容,这个命令会将对应的stash内容清除掉。
$ git stash pop
Feature分支——强行删除分支
如果在master分支上删除一个已经提交但没有合并的其它分支,则会报错:
$ git branch -d f5
error: The branch ‘f5’ is not fully merged.
If you are sure you want to delete it, run ‘git branch -D f5’.
这时可以用参数 -D 强制删除:
$ git branch -D f5
需要注意的是,由于分支未合并,删除之后就没有任何记录了,分支上所有的修改也会丢失。
多人协作
先来两个命令:
$ git remote
查看远程仓库名称
$ git remote -v
查看远程仓库更详细的信息
场景:我本地有master和dev两个分支,但我只把master推送到远程仓库中。然后我的小伙伴从远程的master分支上克隆了一份,他是看不到我本地dev分支的。然后自己在本地新建dev分支进行开发,完了之后推送到远程仓库。同时我在本地 的dev分支修改了跟他一样的文件。这时我准备推送代码,就会报错,提示先pull同步代码, 但拉取的时候又报错,说没有指定本地dev和远程origin/dev之间的连接( There is no tracking information for the current branch. )。可通过以下代码进行关联:
$ git branch –set-upstream-to=origin/dev dev
然后再pull,解决冲突,再提交,再push,跟前面一样。
补充 :从远程git仓库里的指定分支拉取到本地(本地不存在的分支)
git checkout -b 本地分支名 origin/远程分支名
然后再 pull
Rebase
Rebase用来整理提交记录,把多条分叉合并成一条直线。
假设有代码:
1 | $ git log --graph --pretty=oneline --abbrev-commit |
Git用(HEAD -> master)和(origin/master)标识出当前分支的HEAD和远程origin的位置分别是582d922 add author和d1be385 init hello,本地分支比远程分支快两个提交。假设推送时发现有人改了同样文件导致冲突,pull下来解决后再提交,这时本地分支会比远程超前3个提交。
1 | $ git log --graph --pretty=oneline --abbrev-commit |
如果觉得这种分叉的图形看起来乱,可以用如下命令整理一下:
$ git rebase
整理后查看到的记录为:
1 | $ git log --graph --pretty=oneline --abbrev-commit |
发现Git把我们本地的提交“挪动”了位置,放到了f005ed4 (origin/master) set exit=1之后,这样,整个提交历史就成了一条直线。修改不再基于d1be385 init hello,而是基于f005ed4 (origin/master) set exit=1,但最后的提交7e61ed4内容是一致的。推送之后如图:
1 | $ git log --graph --pretty=oneline --abbrev-commit |
远程和本地都成了一条直线。
Rebase的缺点是会更改我们的本地提交,但合并后的内容是一致的。
标签管理
Git的标签就是版本库的快照,但其实它就是指向某个commit的指针。
创建标签
1 | $ git tag v1.0 |
给当前的commit打上标签 v1.0
1 | $ git tag |
查看所有标签,经测试在dev分支上能看到master上所有标签,尽管dev上面的commit要少很多。
注意,标签不是按时间顺序列出,而是按字母排序的
1 | $ git tag v0.9 f52c633 |
可以给历史commit打上标签,最后一个参数是commit的前几位(通过git log查看)
1 | $ git tag -a v0.1 -m "version 0.1 released" 1094adb |
创建带有注释的标签,-a后面是标签名,-m后面是注释内容
1 | $ git show v0.1 |
查看标签名为 v0.1的详细内容
操作标签
1 | $ git tag -d v0.1 |
删除本地标签
1 | $ git push origin v1.0 |
将标签 v1.0 推送到远程仓库
1 | $ git push origin --tags |
将尚未推送的标签全部推送到远程仓库
如果要删除远程仓库的标签,有以下两个步骤:
- 删除本地标签,见上
- 删除远程仓库对应标签 $ git push origin :refs/tags/v0.9