git学习

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 "提交信息"

注意

提交新文件和提交修改都是addcommit

工作区

要随时掌握工作区的状态,使用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
2
3
4
5
6
7
8
9
┌────┐
│HEAD│
└────┘

└──> ○ append GPL

○ add distributed

○ wrote a readme file

改为指向add distributed

1
2
3
4
5
6
7
8
9
┌────┐
│HEAD│
└────┘

│ ○ append GPL
│ │
└──> ○ add distributed

○ wrote a readme file

然后顺便把工作区的文件更新了。所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。

工作区和暂存区

工作区:就是你电脑里能看到的目录,就是你在哪个目录里init,哪个目录就是你的工作区

版本库:就是.git 隐藏目录

因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。

在工作区创建新文件状态是Untracked

一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:

1
2
3
$ git status
On branch master
nothing to commit, working tree clean

管理修改

首先要理解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 commitgit 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
2
3
$ git add test.txt

$ git commit -m "add test.txt"

rm test.txt

当你删除工作区的某个文件(已提交),再用git status

Git会提示你test.txtdeleted状态

第一种情况是你确实要删除版本库中的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严格来说不是指向提交,而是指向mastermaster才是指向提交的,所以,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
2
$ git branch dev
$ git checkout 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分支各自都分别有新的提交,变成了这样:

mastergit merge feature1发现冲突不允许合并。

查看冲突文件会看到

1
2
3
4
5
git add readme.txt <<<<<<< HEAD
Creating a new branch is quick & simple.
=======
Creating a new branch is quick AND simple.
>>>>>>> feature1

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分支上合并就可以了。

所以,团队合作的分支看起来就像这样:

-------------要说再见啦感谢大佬的光临~-------------