keep try
学习廖雪峰老师的git教程总结
https://www.liaoxuefeng.com/wiki/896043488029600/897013573512192
创建版本库
首先,选择一个合适的地方,创建一个空目录
其次,git init
把这个目录变成Git可以管理的仓库
init命令会在工作区创建一个名为.git的隐藏文件,并只在其中创建一个版本库(实质是存放散列值对象引用的存储结构) ls -a可以查看隐藏的.git
一个带版本库的项目目录,我们称之为工作区
把修改的文件放到git版本库(Local Repo)(两步)
git add file1.txt file2.txt
同时commit可以一次提交多个文件
git commit -m "提交信息"
注意
提交新文件和提交修改都是add
和commit
工作区
要随时掌握工作区的状态,使用git status
命令。
如果git status
告诉你有文件被修改过,用git diff
可以查看修改内容。
版本(一次commit就是一次版本,就是一个快照)回退
版本回退前 git log --pretty=oneline
查看一下当前的版本库
注意 那一串字符就是该版本号(commit id)
使用git reset --hard HEAD^
回退到上一个版本
这里的HEAD有必要解释一下 在Git中,用
HEAD
表示当前版本,上一个版本就是HEAD^
,上上一个版本就是HEAD^^
,当然往上100个版本写100个^
比较容易数不过来,所以写成HEAD~100
。
除了使用HEAD来回退,还可以用commit id来回退
可以通过git reset --hard commit_id
实现回退到具体的版本
git reflog
可以用来查看命令历史,可以取到你需要的commit_id
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD
指针,当你回退版本的时候,Git仅仅是把HEAD重新指向append GPL
1 | ┌────┐ |
改为指向add distributed
:
1 | ┌────┐ |
然后顺便把工作区的文件更新了。所以你让HEAD
指向哪个版本号,你就把当前版本定位在哪。
工作区和暂存区
工作区:就是你电脑里能看到的目录,就是你在哪个目录里init,哪个目录就是你的工作区
版本库:就是.git 隐藏目录
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master
分支,所以,现在,git commit
就是往master
分支上提交更改。
在工作区创建新文件状态是Untracked
一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:
1 | $ git status |
管理修改
首先要理解Git
中的修改
比如你新增了一行,删除了一行,更改了某些字符,删了一些又加了一些,甚至创建一个新文件,删除一个文件,也算一个修改。
要理解Git管理的是修改,而不是文件。
如下操作
第一次修改readme.txt ->
git add
-> 第二次修改readme.txt ->git commit
当你git status
查看会发现readme.txt
依然是modified
状态。
通过git diff HEAD -- readme.txt
(该命令可以查看工作区和版本库里面最新版本的区别)
可以看到第二次修改并没有被提交,需要你 git add
+ git commit
一波。
撤销修改
丢弃工作区的修改
1 | git checkout -- readme.txt |
git checkout -- readme.txt
意思就是,把readme.txt
文件在工作区的修改全部撤销,这里有两种情况:
一种是
readme.txt
自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;一种是
readme.txt
已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。总之,就是让这个文件回到最近一次
git commit
或git add
时的状态。
注意
命令中的--
很重要,没有--
,就变成了“切换到另一个分支”的命令。
看看第二种add之后的情况
git add readme.txt
命令之后git会提示你使用git reset HEAD <file>
撤销(unstage
),重新放回工作区。
git reset
命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD
时,表示最新的版本。
会发现使用git reset HEAD readme.txt只是把暂存区的修改回退到了工作区,此时工作区的readme.txt依然是修改过的。
通过git checkout
可以把回退到工作区的修改丢弃啦,哈哈哈。
如果已经commit
了,那就只能用git reset
版本回退了 。
删除文件
1 | $ git add test.txt |
rm test.txt
当你删除工作区的某个文件(已提交),再用git status
Git会提示你test.txt
是deleted
状态
第一种情况是你确实要删除版本库中的test.txt
git rm test.txt
git commit -m "remove test.txt"
文件就从版本库中被删除了
另一种情况是删错了
此时版本库还有该文件
git checkout -- test.txt
实质是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
注意 从来没有被添加到版本库就被删除的文件,是无法恢复的!
远程仓库
一个疑问 – 为什么GitHub需要SSH Key
因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
最后友情提示,在GitHub上免费托管的Git仓3库,任何人都可以看到喔(但只有你自己才能改)。所以,不要把敏感信息放进去。
如果你不想让别人看到Git库,有两个办法,一个是交点保护费,让GitHub把公开的仓库变成私有的,这样别人就看不见了(不可读更不可写)。另一个办法是自己动手,搭一个Git服务器,因为是你自己的Git服务器,所以别人也是看不见的。这个方法我们后面会讲到的,相当简单,公司内部开发必备。
工作情景
情景一
已经在本地创建了一个Git仓库(仓库名为learnGit)后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作。
github —— create a new repo —— Repository name填入learnGit,其他保持默认设置
现在,我们根据GitHub的提示,在本地的learnGit
仓库下运行命令:
1 | $ git remote add origin git@github.com:thinkerHope/learngit.git |
注意
thinkerHope
是你自己的github
账户名,否则,你虽然可以关联我的github
仓库,但是不可以推送内容。因为你的SSH Key公钥不在我的账户列表中。
添加后,远程库的名字就是origin
,这是Git默认的叫法,也可以改成别的,但是origin
这个名字一看就知道是远程库。
下一步,就可以把本地库的所有内容推送到远程库上:
1 | $ git push -u origin master |
-u参数解释
由于远程库是空的,我们第一次推送master
分支时,加上了-u
参数,Git不但会把本地的master
分支内容推送的远程新的master
分支,还会把本地的master
分支和远程的master
分支关联起来,在以后的推送或者拉取时就可以简化命令。
从现在起,只要本地作了提交,就可以通过命令:
1 | $ git push origin master |
把本地master
分支的最新修改推送至GitHub,现在,你就拥有了真正的分布式版本库!
前景二
先创建远程库,然后,从远程库克隆。(从零开发项目的最佳选择)
create a new repo (名字为gitskills
) -> 勾选Initialize this repository with a README
—> git clone
—> git clone git@github.com:thinkerHope/gitskills.git
->cd gitskills
+ ls
就可以看到README.md
创建与合并分支
每次提交(commit),Git都把它们串成一条时间线,这条时间线就是一个分支。之前的HEAD严格来说不是指向提交,而是指向
master
,master
才是指向提交的,所以,HEAD
指向的就是当前分支。
每次提交,master
分支都会向前移动一步,这样,随着你不断提交,master
分支的线也越来越长。
当我们创建新的分支,例如dev
时,Git新建了一个指针叫dev
,指向master
相同的提交,再把HEAD
指向dev
,就表示当前分支在dev
上。
Git创建一个分支很快,因为除了增加一个dev
指针,改改HEAD
的指向,工作区的文件都没有任何变化。
不过,从现在开始,对工作区的修改和提交就是针对dev
分支了,比如新提交一次后,dev
指针往前移动一步,而master
指针不变:
假如我们在dev
上的工作完成了,就可以把dev
合并到master
上。Git怎么合并呢?最简单的方法,就是直接把master
指向dev
的当前提交,就完成了合并。
合并完分支后,甚至可以删除dev
分支。删除dev
分支就是把dev
指针给删掉,删掉后,我们就剩下了一条master
分支
git checkout -b dev
创建dev
分支,然后切换到dev
分支,加上-b
参数表示创建并切换。
1 | $ git branch dev |
git branch
列出所有分支,当前分支前面会标一个*
号。
在dev分支对readme.txt做修改并提交后,再放回master分支,会发现工作区的修改没有了,因为提交在dev分支上,master分支此刻的提交点没有变。
Q 有个问题需要思考一下 工作区的文件会随着版本库的指针改变。
git merge dev
回到master分支把
dev
分支的工作成果合并到master
分支上
然后就可以放心地删除dev分支了。
上图有个Fast-forward
,Git告诉我们,这次合并是“快进模式”,也就是直接把master
指向dev
的当前提交,所以合并速度非常快。
当然,也不是每次合并都能Fast-forward
,我们后面会讲其他方式的合并。
解决冲突
创建一个feature
分支(刚创建的时候和master
指向同一个版本库),做好修改并且提交。
又回到master分支上,也做修改并且提交。
现在,master
分支和feature1
分支各自都分别有新的提交,变成了这样:
在master
上git merge feature1
发现冲突不允许合并。
查看冲突文件会看到
1 | git add readme.txt <<<<<<< HEAD |
Git用
<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容
需要自己手动修改后再git add
+ git commit
一波。
现在,master
分支和feature1
分支变成了下图所示:
git log --graph --pretty=oneline --abbrev-commit
可以看到分支的合并情况。
小结:解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。
分支管理策略
--no-ff
(fast forward模式在删除分支后,会丢掉分支信息)方式的git merge
。
普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而
fast forward
合并就看不出来曾经做过合并。
测试
和最初我创建合并分支一样
git checkout -b dev
之后就是一波git add
+ git commit
git checkout master
最后git merge --no-ff -m "merge with no-ff" dev
和ff模式做个对比
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样: