Bootstrap

Git入门笔记

本文完全来自廖雪峰老师的 Git教程
为方便回顾以及每日拜读,固作此笔记
廖雪峰老师的Git教程真的十分精彩,建议初学者直接点击上方链接到<廖雪峰的官方网站>进行学习。

Git Cheat Sheet


1. Git简介

1.1 什么是Git

Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理项目。
Git是Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
Git的诞生很有趣,康康

1.2 集中式vs分布式

集中式版本控制系统:版本库集中存放在 中央服务器 ,工作时从中央服务器获取最新版,干完再推送到中央服务器。最大毛病在于必须 联网 工作,网速成为大问题。例:CVS、SVN
分布式版本控制系统:没有“中央服务器”,每个人的电脑都是一个完整的版本库,修改后只需要把修改推送给对方,即可看到对方的修改。相比之下 安全 性更高,因为中央服务器出问题所有人就没法干活。例:Git、促使Git诞生的BitKeeper、Mercurial和Bazaar
分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改

2. Git安装

2.1 Linux

输入git,查看是否安装git
若未安装
sudo apt-get install git(Debian或Ubuntu系统)

2.2 Windows

Git官网下载

安装完成后打开git bash
输入

git config --global user.name "Name"
git config --global user.email "Your Email"

--global参数表示机器上所有Git仓库都使用这个配置

3. 创建版本库

3.1 版本库

什么是版本库?版本库又名仓库(repository)
可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。

3.2 创建

1、创建一个空目录
注意:目录名不要含中文

2、git init
初始化仓库,使目录变成Git可管理的仓库
此时会多出一个.git目录,用来跟踪管理版本库的,不要乱改,否则会破坏仓库

注意:所有的版本控制系统只能跟踪文本文件的改动,对于图片、视频具体改了什么是不知道的,而且也没法跟踪word文件的改动,因为word是二进制格式

3、编写一个readme.txt文件

4、git add

git add readme.txt

把文件添加到仓库

git add .

也可添加所有文件

5、git commit

git commit -m "a readme file"

把文件提交到仓库

-m 后输入的是本次提交的说明
add添加不同文件,commit一次提交文件

4. 时光穿梭机

4.1 状态查看

要随时掌握工作区的状态,使用git status命令。可以了解当前仓库是否有文件被修改或是否有将被提交的修改

如果git status告诉你有文件被修改过,用git diff可以查看修改内容。

4.2 版本回退

当修改文件到一定程度,就可以“保存一个快照”,这个快照Git称为commit,一旦文件改乱了或误删了,就可以从最近的一个commit恢复

git log 查看历史修改记录
git log --pretty=oneline 简略记录
关于输出,有一大串commit id(版本号),防止多人在同一版本库内工作起冲突

每提交一个新版本,实际上Git就会把它们自动串成一条时间线

启动时光穿梭机!

git reset --hard HEAD^

回退到上一个版本
HEAD表示当前版本,多一个 ^代表回退多一个版本,如HEAD^^ 回退到上上个版本
HEAD~100 回退100个版本

但是回退后再git log就看不到从此之后的版本了,好比回到过去却回不来了
别急,只要当前窗口没关,往上找到之前git log里往后的commit id就可以了
再次git reset --hard id号
版本号较长,没有必要写全,写前几位即可

当然,关了也是有后悔药吃的,git reflog即可看到之前所有操作的版本号。在git内部其实并没有把版本删掉,只是用HEAD指针指向当前版本,然后更新工作区文件,所以看不到此后的版本

4.3 工作区和暂存区

工作区与版本库关系
工作区:电脑的工作目录,不含.git目录
版本库:.git目录
暂存库:版本库下stage(或者叫index)的文件
版本库还包含Git为我们自动创建的第一个分支master,和指向master的指针HEAD

add 和 commit 的作用如图所示
总之,git add 命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit就可以一次性把暂存区的所有修改提交到分支

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

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

”干净“的工作区

4.4 管理修改

为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件。

第一次修改 -> git add -> 第二次修改 -> git commit
git status,此时只会发现第一次修改的记录,而第二次修改并没提交

git diff HEAD -- filename命令可以查看工作区和版本库里面最新版本的区别,第二次修改并没被提交

因此,流程应如下:
第一次修改 -> git add -> 第二次修改 -> git add -> git commit

4.5 撤销修改

1、情况一
git checkout -- readme.txt
把readme.txt文件在工作区的修改全部撤销,这里有两种情况:
一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commitgit add时的状态。
注意:git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令

2、情况二
git reset HEAD <file>
把暂存区的修改撤销掉(unstage),重新放回工作区
git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本

3、情况三
从暂存区提交到版本库
需要参考 4.2 版本回退

4、情况四
推送到远程
没法在本地解救了

4.6 删除文件

删除工作区和版本库的同一文件
在工作区git rmgit commit

若在工作区误删了,使用git checkout来还原(有被添加到版本库过)

5. 远程仓库

以上的功能基本上和集中式版本控制SVN一样,但Git的杀手级功能在于:远程仓库
Github网站可以提供免费的Git仓库托管服务的远程仓库

由于本地GIt仓库和Github仓库之间的传输是通过SSH加密的,需要进行设置:
1、创建SSH Key
Git Bash打开,输入

ssh-keygen -t rsa -C "[email protected]"

接下来默认回车,在用户主目录下会生成.ssh目录,其中id_rsa是私钥,id_rsa.pub是公钥。

2、github 添加公钥
登陆GitHub,打开“Account settings”,“SSH Keys”页面:
然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容

为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
当然,GitHub允许你添加多个Key

5.1 添加远程仓库

1、登陆GitHub,然后,在右上角找到“Create a new repo”按钮,创建一个新的仓库
在Repository name填入仓库名,其他保持默认设置,点击“Create repository”按钮,就成功地创建了一个新的Git仓库
创建新仓库
2、关联Github仓库
在本地仓库下运行

git remote add origin [email protected]:username/repositoryname.git

添加后,远程库的名字就是origin,这是Git默认的叫法,也可以改成别的,但是origin这个名字一看就知道是远程库

git push -u origin master

把本地库的内容推送到远程,用git push命令,实际上是把当前分支master推送到远程
由于远程库是空的,我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。

3、删除远程库

git remote rm origin

此处的“删除”其实是解除了本地和远程的绑定关系

使用前,建议先用

git remote -v

查看远程库信息

5.2 从远程库克隆

git clone [email protected]:username/repositoryname.git

Git支持多种协议,默认的git://使用ssh,但也可以使用https等其他协议。

使用https除了速度慢以外,还有个最大的麻烦是每次推送都必须输入口令,但是在某些只开放http端口的公司内部就无法使用ssh协议而只能用https

6. 分支管理

分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。

现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既安全,又不影响别人工作。

Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件

6.1 创建与合并分支

初始只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支

一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点
在这里插入图片描述

当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上在这里插入图片描述
Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!

从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变在这里插入图片描述

假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并
在这里插入图片描述

合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支
在这里插入图片描述

1、创建、切换分支

git checkout -b dev

创建dev分支,然后切换到dev分支
-b 表示创建并切换
相当于

git branch dev
git checkout dev

2、查看分支

git branch

命令查看所有分支,带*号的为当前分支

git checkout master

切换回master主分支
此时发现刚才添加的内容不见了,是因为提交的是在dev分支上,而master分支此刻的提交点并没有变
在这里插入图片描述

3、合并分支

git merge dev

合并指定分支到当前分支

Fast-forward (快进模式):直接把master指向dev的当前提交,所以合并速度非常快

4、删除分支

git branch -d dev

新版本Git的另一种创建并切换分支方式

git switch -c dev

6.2 解决冲突

在这里插入图片描述
当master和feature1分支下都各有修改且不同并提交,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。可以通过git status查看冲突文件

存在冲突,必须手动解决冲突后再提交
打开冲突文件,会有<<<<<<<=======>>>>>>>标记出不同分支的内容
修改后再提交即可
在这里插入图片描述

用带参数的git log也可以看到分支的合并情况
最后删除分支

当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。
用git log --graph命令可以看到分支合并图。

6.3 分支管理策略

通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。

如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。

git merge --no-ff -m "merge with no-ff" dev

--no-ff参数,表示禁用Fast forward
-m参数,把commit描述写进去

不使用Fast forward模式,merge后就像这样
在这里插入图片描述

在实际开发中,我们应该按照几个基本原则进行分支管理:

首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;

那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;

你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。

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

在这里插入图片描述

合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并

6.4 Bug分支

开发中,bug若家常便饭,每一个bug都可以通过一个新的临时分支来修复,修复后再合并分支,然后删除临时分支

1、git stash 保存现场
但如果此时你正在进行的工作还没有提交,可以通过git stash来把当前工作现场“储藏”起来,等以后恢复现场继续工作
保存现场后,git status查看工作区,是干净的

2、创建临时分支
接下来,确定在哪个分支上修复bug,就切换到哪个分支上创建临时分支

3、修复完成后
切换回原分支,完成合并,之后删除临时分支

4、恢复工作现场
查看保存的工作现场git stash list

$ git stash list
stash@{0}: WIP on dev: f52c633 add merge

恢复方法有二
方法一:git stash apply恢复,但恢复后stash内容并不删除,用git stash drop来删除
方法二:git stash pop,恢复同时删除

多次stash,恢复时指定
git stash apply stash@{0}

5、cherry-pick
假设在主分支上修复bug,但dev分支是早期从主分支上分出来的,所以也存在bug
可以通过cherry-pick命令去复制某个特定的提交到当前分支,而不需要去重复操作修改一次

git cherry-pick <commit>
<commit>是修复bug所做的提交的commit

另外也可以在dev分支上做修复而不去创建临时分支,然后再在主分支上“重放”

6.5 Feature分支

软件开发中,总有无穷无尽的新的功能要不断添加进来。

添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。

当合并前突然要销毁所有所做的,git branch -d feature是销毁不了的,必须使用
git branch -D feature
强行删除

6.6 多人协作

当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin

查看远程库信息git remote
更详细信息git remote -v
(fetch)可抓取,(push)可推送

1、推送分支
将本地提交推送到远程库,推送时要指定本地分支
git push origin master
推送其他分支的话,修改master为其他分支名

master分支是主分支,因此要时刻与远程同步;
dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。

2、抓取分支
当其他小伙伴clone远程库时,默认情况下只能看到master分支,若要在dev分支上开发,就要创建远程origindev分支到本地

git checkout -b dev origin/dev

dev上修改后,把dev分支push到远程

此时你也对同样的文件作了修改,试图推送但失败,因为小伙伴的最新提交和你试图推送的提交有冲突

把最新提交从origin/dev抓下来,在本地合并,解决冲突再推送

git pull 会失败,因为本地dev分支没有与远程origin/dev分支链接

设置devorigin/dev的链接

git branch --set-upstream-to=origin/dev dev

设置后在git pull就可以了

然后手动解决冲突,提交,push

3、多人协作的工作模式通常是这样:

1)首先,可以试图用git push origin <branch-name>推送自己的修改;

2)如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;

3)如果合并有冲突,则解决冲突,并在本地提交;

4)没有冲突或者解决掉冲突后,再用git push origin <branch-name>推送就能成功!

如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>

6.7 Rebase

多人协作下,Git的提交历史将不是条干净的直线,对我们查看历史提交的变化带来麻烦,因为分叉的提交需要多方对比

rebase操作可以把本地未push的分叉提交历史整理成直线
git rebase

缺点是本地的分叉提交已经被修改过了

7. 标签管理

发布一个版本时,通常先在版本库打一个标签(tag)来唯一确定打标签时刻的版本。
将来需要取某个标签版本时可将打标签时刻的历史版本取出来。相当于版本库的一个快照。

标签是指向某个commit的指针(跟分支很像,但是分支可以移动,标签不能移动)
创建和删除标签都是瞬间完成的

7.1 创建标签

1、切换到打标签的分支
2、打标签

git tag v1.0

v1.0为标签

git tag查看所有标签

标签默认打在最新提交的commit上
若要打在历史commit上

git log --pretty=oneline --abbrev-commit

查看历史提交的commit id

然后

git tag v0.9 <commit id>

创建带有说明的标签,用-a指定标签名,-m指定说明文字

git tag -a v0.1 -m "version 0.1 released" <commit id>

3、查看标签信息
标签不是按时间顺序列出,而是按字母排序的。可以用git show <tagname>查看标签信息

7.2 操作标签

1、删除标签
git tag -d v0.1

2、推送标签到远程
创建的标签只存储在本地,不会自动推送到远程
git push origin <tagname>

一次性推送全部尚未推送到远程的本地标签
git push origin --tags

3、删除远程标签
先删除本地标签
然后
git push origin :refs/tags/v0.1

登陆GitHub查看是否删除

8. 使用Github

GitHub作为免费的远程仓库,如果是个人的开源项目,放到GitHub上是完全没有问题的。GitHub还是一个开源协作社区,通过GitHub,既可以让别人参与你的开源项目,也可以参与别人的开源项目。

参与一个开源项目,在项目主页点“Fork”就可以clone到自己账号,然后从自己的账号下clone到本地,这样才能推送修改。

如果直接从他人仓库克隆下来,由于没有权限,是推送不成功的。

修改项目后推送到自己仓库,然后发起pull request,看对方是否接受你的修改

9.使用Gitee

由于Github网速问题,国内的Gitee会好很多

和GitHub相比,Gitee也提供免费的Git仓库。此外,还集成了代码质量检测、项目演示等功能。对于团队协作开发,Gitee还提供了项目管理、代码托管、文档管理的服务,5人以下小团队免费。

1、注册添加SSH公钥
与github操作类似

2、创建远程仓库

3、关联本地和远程库
git remote add origin [email protected]:username/repositoryname.git

4、删除远程库
git remote rm origin

5、关联多个远程库
只要远程库名不一样就可以

10. 自定义Git

除了配置user.nameuser.email,Git还有许多配置项

如让Git显示颜色
git config --global color.ui true

10.1 忽略特殊文件

有时需要把某些文件放到Git工作目录下,但又不能提交,比如数据库密码的配置等等。在每次git status都会显示Untracked files ...

可以在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件

不需要从头写.gitignore文件,GitHub已经为我们准备了各种配置文件,只需要组合一下就可以使用了。所有配置文件可以直接在线浏览:https://github.com/github/gitignore

忽略文件的原则是:

忽略操作系统自动生成的文件,比如缩略图等;
忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。

1、创建.gitignore文件
2、添加隐藏的文件名

# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini

# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build

# My configurations:
db.ini
deploy_key_rsa

#注释

被忽略的文件想添加到GIt
git add -f file
强制添加

3、例外规则

# 排除所有.开头的隐藏文件:
.*
# 排除所有.class文件:
*.class

# 不排除.gitignore和App.class:
!.gitignore
!App.class

4、添加.gitignore到版本库
当有.*规则时要添加!.gitignore
.gitignore文件本身要放到版本库里,并且可以对.gitignore做版本管理!

5、检查规则
git check-ignore

$ git check-ignore -v App.class
.gitignore:3:*.class	App.class

.gitignore的第3行规则忽略了该文件

10.2 配置别名

git config --global alias.st status
statusst代替

co checkout
ci commit
br branch
unstage 'reset HEAD'
last 'log -1'
lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

每个仓库的Git配置文件都放在.git/config文件中

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true
[remote "origin"]
    url = [email protected]:michaelliao/learngit.git
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
    remote = origin
    merge = refs/heads/master
[alias]
    last = log -1

别名就在[alias]后面,要删除别名,直接把对应的行删掉即可

当前用户的Git配置文件放在用户主目录下的一个隐藏文件.gitconfig

[alias]
    co = checkout
    ci = commit
    br = branch
    st = status
[user]
    name = Your Name
    email = [email protected]

10.3 搭建Git服务器

GitHub就是一个免费托管开源代码的远程仓库。但是对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,那就只能自己搭建一台Git服务器作为私有仓库使用。

搭建Git服务器需要准备一台运行Linux的机器,强烈推荐用Ubuntu或Debian,这样,通过几条简单的apt命令就可以完成安装。

1、安装git

sudo apt-get install git

2、创建git用户,来运行git服务

sudo adduser git

3、创建证书登陆
收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。

4、初始化Git仓库
先选定一个目录为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令

sudo git init --bare sample.git

Git就会创建一个裸仓库,裸仓库没有工作区,因为服务器上的Git仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的Git仓库通常都以.git结尾。然后,把owner改为git

sudo chown -R git:git sample.git

5、禁用shell登陆
出于安全考虑,第二步创建的git用户不允许登录shell,这可以通过编辑/etc/passwd文件完成。找到类似下面的一行

git:x:1001:1001:,,,:/home/git:/bin/bash

改为

git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell

这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出

6、克隆远程仓库

$ git clone git@server:/srv/sample.git
Cloning into 'sample'...
warning: You appear to have cloned an empty repository.

管理公钥
如果团队很小,把每个人的公钥收集起来放到服务器的/home/git/.ssh/authorized_keys文件里就是可行的。如果团队有几百号人,就没法这么玩了,这时,可以用Gitosis来管理公钥。

管理权限
有很多不但视源代码如生命,而且视员工为窃贼的公司,会在版本控制系统里设置一套完善的权限控制,每个人是否有读写权限会精确到每个分支甚至每个目录下。因为Git是为Linux源代码托管而开发的,所以Git也继承了开源社区的精神,不支持权限控制。不过,因为Git支持钩子(hook),所以,可以在服务器端编写一系列脚本来控制提交等操作,达到权限控制的目的。Gitolite就是这个工具

;