Git简介
Git是分布式的版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理
Git的分区
Git分为三个区:工作区、暂存区和本地仓库(版本库)
Git全局配置
安装完Git后,需要配置Git的用户名和邮箱,使用git config
命令进行配置
git config --global user.name "Your Name"
git config --global user.email "Your Email"
配置SHH key
-
创建SSH Key。先在用户目录查看是否有
.ssh
文件夹,如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件。如果有则直接进行下一步,没有则进行创建ssh-keygen -t rsa -C "youremail@example.com"
id_rsa
是私钥,不能泄露出去,id_rsa.pub
是公钥,可以放心地告诉任何人。 -
登陆GitHub,打开“Account settings”,“SSH Keys”页面:
然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴
id_rsa.pub
文件的内容:点“Add Key”,你就应该看到已经添加的Key
创建版本库(初始化)
通过git init
命令可以将一个目录(项目)使用Git来管理
git init
Git常见命令
git add
使用命令git add <file>
,可以将工作区文件添加到暂存区
# 添加单个文件
git add a.js
# 添加所有改动过的文件
git add .
# 添加参数-p,代表对单个文件进行分割提交
git add -p file.txt
git commit
使用命令git commit -m <message>
,可以将暂存区文件提交到本地仓库,会产生一次提交记录
# -m 后是本次提交的描述信息
git commit -m '完成了首页功能'
# 参数 --amend 可以修改最后一次提交信息,可以覆盖提交
git commit --amend -m '修改了b.txt'
# 参数 --no-edit 可以不添加描述信息
git commit --no-edit
# 参数 -a 可以直接将工作区改动的文件直接提交到本地仓库
git commit -a -m 'message'
git commit -am 'message'
# 参数 --date 可以修改提交时间
git commit --amend --no-edit --date="1987-08-03"
git status
使用命令git status
可以查看工作区的状态
git status
当提示为红色的modified时,代表该文件在工作区有改动,但没有添加到暂存区。
当提示为绿色的modified时,代表该文件在暂存区有改动,但没有提交到本地仓库。
git diff
使用该命令可以查看文件修改的对比,可具体查看不同之处
# 查看工作区(已被track但未add文件)和暂存区的区别,如果暂存区该文件不存在,则与本地仓库最后一次提交对比
git diff <filename>
# 查看暂存区(已add但未commit)和本地仓库(最后一次commit)的区别
git diff --cached
# 查看工作区(已被track但未add文件)和暂存区(已add但未commit)与最后一次commit提交对比 ---- 可查看所有被修改过的文件和本地仓库最新提交的区别
git diff HEAD
# 比较最新一次提交和之前提交的区别
git diff HEAD~3
git diff HEAD <commit-id>
# 比较两个分支最新提价的区别
git diff <branch1> <branch2>
git diff <branch1> <branch2> --stat
# --stat 只显示区别的文件,不显示具体内容
git log
使用该命令可以查看历史提交日志,HEAD指向之前
git log
# 使用参数 --pretty=oneline 可以简化信息
git log --pretty=oneline
# 使用参数 --graph 可以查看分支合并图
git log --graph
# 使用参数 --abbrev-commit 可以省略commit id
git log --abbrev-commit
git reflog
使用该命令可以查看所有命令历史,包括提交,回退等
git reflog
git reset
reset --hard:重置暂存区和工作区: 完全回到本地仓库的状态
# 回到上个版本
git reset --hard HEAD^
# 回到指定的版本
git reset --hard <commit id>
reset --soft:保留工作目录,并把重置 HEAD 所带来的新的差异放进暂存区
git reset --soft HEAD^
reset 不加参数(mixed):保留工作目录,并将重置 HEAD 所带来的新的差异放入工作区
# 默认不加参数就是mixed模式
git reset HEAD^
git revert
使用该命令可以撤回之前某次提交的内容,并向前提交一个版本,HEAD指针向前一步
换句话说,使用git revert
可以撤回某次的提交,但又能保留之后的内容
git rm
使用该命令可以删除工作区和暂存区的文件
# 删除工作区文件
git rm <filename>
# 强制删除已经add到暂存区的文件
git rm -f <filename>
# 单独删除add到暂存区文件,保留工作区文件(使其变为untrack)
git rm --cached <filename>
git mv
使用该命令可以移动文件或修改文件名
git mv <filename> <newFIle>
git restore
使用该命令可以撤销一些操作
# 撤销从工作区添加到暂存区的文件,保留修改
git restore --staged <filename>
# 撤销对工作区文件的修改(已被track,但未add文件)
git restore <filename>
git checkout
该命令有两个功能,第一个功能就是撤销工作区的修改,将当前工作区回退到上一次git add
或者git commit
状态
- 如果该文件自修改后没有放入暂存区,那么就回调和本地仓库一样的状态
- 如果该文件修改后添加到暂存区后又修改了,那么就回到上一次添加到暂存区后的状态
# 将文件回到上一次添加到暂存区的状态,撤销未add的修改
git checkout -- <filename>
# 将文件回到HEAD中的状态,撤销未commit的修改
git checkout HEAD <filename>
如果使用该命令没有加--
则是另一个功能,切换分支功能
# 切换分支
git checkout dev
# 创建分支并切换到该分支
git checkout -b dev
# 创建本地分支并关联远程分支
git checkout -b branch-name origin/branch-name
git switch
该命令可以切换分支
git switch dev
也可以创建分支并切换到该分支
git switch -c feature
创建本地分支并关联远程分支
git switch -c fix-bug origin/fix-bug
git branch
# 查看分支
git branch
# 查看所有分支,包含隐藏分支
git branch -a
# 创建分支
git branch <name>
# 删除分支
git branch -d <name>
# 强制删除未合并分支
git branch -D <name>
# 本地分支关联远程分支
git branch --set-upstream-to <branch-name> origin/<branch-name>
git stash
该命令可以将当前暂存区的代码存储起来,之后可以恢复
# 将当前工作内容贮藏起来
git stash
# 查看贮藏区内容
git stash list
# 可以取回指定贮藏区内容
git stash apply stash@{0}
# 取回贮藏区最新内容,并删除
git stash pop
# 删除贮藏区内容
git stash drop stash@{0}
git tag
可以给某个提交打上标签
# 查看所有标签
git tag
# 在当前分支最新提交上打标签
git tag <name>
# 给某个提交打标签
git tag <name> <commit-id>
# 查看某个标签具体信息
git show <name>
# 创建带说明的标签
git tag -a <tagname> -m 'commit message'
# 删除本地标签
git tag -d <tagname>
# 推送某个标签至远程
git push origin <tagname>
# 推送所有未被推送的标签至远程
git push origin --tags
# 删除远程标签
git push origin :refs/tags/<tagname>
git cherry-pick
使用该命令,可以将某次提交的修改“复制”到当前分支,避免重复劳动。
git cherry-pick <commit-id>
git merge
该命令可以合并某分支到当前分支,如果没有冲突,默认使用Fast forward
模式
# 默认快速合并
git merge <name>
# 使用普通合并 --no-ff 会产生一个新的提交
git merge --no-ff -m '11.合并了feature4分支' feature4
git rebase
该命令一般有两个作用
- 合并代码并将提交历史按分支合并成一条直线
- 可以修改历史提交
例如,有如下情况:
如果使用git merge
命令将dev分支合并到master分支,情况如下:
git merge
会把两个分支的最新快照(D
和 G
)以及二者最近的共同祖先(B
)进行三方合并,合并的结果是生成一个新的快照(并提交)。
其实,还有一种方法:你可以提取在 G
中引入的补丁和修改,然后在 D
的基础上应用一次。 在 Git 中,这种操作就叫做 变基(rebase)。 你可以使用 rebase
命令将提交到某一分支上的所有修改都移至另一分支上,就好像“重新播放”一样。
-
git switch dev git rebase master
-
git switch master git rebase dev
总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史, 从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。
git rebase -i
使用命令git rebase -i
可以进行交互式变基,也就是可以对之前的提交历史进行修改
格式:git rebase -i [startpoint] [endpoint]
其中-i
的意思是–interactive
,即弹出交互式的界面让用户编辑完成合并操作,[startpoint] [endpoint]则指定了一个编辑区间,如果不指定[endpoint],则该区间的终点默认是当前分支HEAD所指向的commit(注:该区间指定的是一个前开后闭的区间)。
变基时有六个命令可用:
-
pick
保留该commit(缩写:p)。重新进行命令时,重新安排pick命令的顺序会更改提交的顺序。如果选择不包括提交,则应删除整行。
-
reword
保留该commit,但我需要修改该commit的注释(缩写:r)。使用后,重新设置过程将暂停并为您提供更改提交消息的机会。提交所做的任何更改均不受影响。
-
edit
如果您选择edit提交,则将有机会修改提交,这意味着您可以完全添加或更改提交。您还可以进行更多提交,然后再继续进行变基。这使您可以将大型提交拆分为较小的提交,或者删除在提交中所做的错误更改。(缩写:e)
-
squash
该命令使您可以将两个或多个提交合并为一个提交。提交被压缩到其上方的提交中。Git使您有机会编写描述这两个更改的新提交消息。(缩写:s)
-
fixup
这类似于squash,但是要合并的提交已丢弃其消息。提交仅合并到其上方的提交中,并且较早提交的消息用于描述这两个更改。缩写:f)
-
exec
这使您可以对提交运行任意的Shell命令。(缩写:x)
-
drop
我要丢弃该commit(缩写:d)
git push
使用该命令可以把当前分支推送到远程仓库
# 第一次push需把本地分支和远程分支关联起来
git push -u origin master
git push
该命令也可以进行其他操作,例如删除远程分支,添加和删除远程标签等
# 推送某个标签至远程
git push origin <tagname>
# 推送所有未被推送的标签至远程
git push origin --tags
# 删除远程标签
git push origin :refs/tags/<tagname>
# 删除远程分支
git push o
git pull
从关联的远程仓库拉取代码
git pull
如果当前分支没有和远程分支关联,需要进行关联
git branch --set-upstream-to <branch-name> origin/<branch-name>
git clone
用命令git clone
可以把远程仓库克隆到本地
git clone <远程仓库地址>
git remote
# 添加关联
git remote add <name> <远程仓库地址>
# 查看关联
git remote -v
# 删除关联
git remote rm <name>
git update
Git进阶
远程仓库
配置SSH key
-
创建SSH Key。先在用户目录查看是否有
.ssh
文件夹,如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件。如果有则直接进行下一步,没有则进行创建ssh-keygen -t rsa -C "youremail@example.com"
id_rsa
是私钥,不能泄露出去,id_rsa.pub
是公钥,可以放心地告诉任何人。 -
登陆GitHub,打开“Account settings”,“SSH Keys”页面:
然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴
id_rsa.pub
文件的内容:点“Add Key”,你就应该看到已经添加的Key
添加关联远程仓库
关联远程仓库
git remote add origin git@github.com:michaelliao/learngit.git
添加后,远程库的名字就是origin
,这是Git默认的叫法,也可以改成别的,但是origin
这个名字一看就知道是远程库。
git remote -v
可以查看远程库信息
用git remote rm <name>
命令可以删除关联
用git push
命令,可以把当前分支推送到远程仓库
从远程仓库克隆
用命令git clone
可以把远程仓库克隆到本地
git clone <远程仓库地址>
分支管理
创建合并分支
查看分支:git branch
创建分支:git branch <name>
切换分支:git switch <name>
创建+切换分支:git switch -c <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
如果要丢弃一个没有被合并过的分支,可以通过git branch -D <name>
强行删除。
-
一开始的时候,
master
分支是一条线,Git用master
指向最新的提交,再用HEAD
指向master
,就能确定当前分支,以及当前分支的提交点 -
当我们创建了一个
dev
分支,并切换到该分支时。dev
和master
都同时指向最新的提交,而HEAD
则指向当前分支dev
-
当我们在
dev
分支上进行了一次新的提交后,dev
分支向前一步,而master
分支则保持不变 -
当我们在
dev
分支上工作完成后,需要把dev
分支合并到master
分支上,此时需要切换到master
分支上,并使用git merge
命令进行合并。合并的方法很简单,就是将master
分支指针直接指向dev
分支当前的提交就行了 -
当不再需要
dev
分支时,我们可以使用命令git branch -d dev
删除dev
分支,删除分支就是把dev
指针删除。此时就剩master
一条分支了
解决冲突
当在master
分支创建一个feature1
分支,然后在这两个分支分别有各自的新提交后,此时就变成如下:
此时,我们将feature1
分支合并到master
分支上
git switch master
git merge feature1
如果master
分支和feature1
分支新的提交同时修改了同一文件的话,就出产生冲突,无法进行快速合并
此时使用git status
命令可以显示出产生冲突的文件,文件信息是both modified
,而且Git用<<<<<<<
,=======
,>>>>>>>
标记出不同分支的内容
此时我们就需要手动去修改突出的部分,然后再次提交
git add .
git commit -m '解决冲突'
现在,master
分支和feature1
分支变成了下图所示:
注意:
- 被合并的
feature1
分支指向是没有发生改变的,因此feature1
分支下的最新提交没有发生改变 - 解决冲突实际上就是产生一个新的提交
分支管理策略
通过之前学习知识,可以知道git在没有冲突的情况下合并采用的是Fast forward
模式,这种合并模式的原理就是将目前分支的指针直接移动到待合并分支的最新提交上。
但是这个合并的缺点就是无法记录合并历史,无法看出是否合并。
我们可以在合并的时候添加--no-ff
参数,禁用Fast forward
模式,采用普通模式
,这Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。
git merge --no-ff -m '11.合并了feature4分支' feature4
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;
你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
所以,团队合作的分支看起来就像这样:
bug分支
当在一个dev
分支下进行开放,突然需要修改master
分支的bug时:
可以使用git stash
命令将当前工作内容贮藏起来
使用git stash list
可以查看贮藏区内容
使用git stash apply stash@{0}
可以取回指定贮藏区内容
使用git stash pop
可以取回贮藏区最新内容,并删除
使用git stash drop stash@{0}
可以删除贮藏区内容
在master分支上修复的bug,想要合并到当前dev分支,可以用git cherry-pick <commit>
命令,把bug提交的修改“复制”到当前分支,避免重复劳动。
多人协作
多人协作工作流程:
- 首先,可以试图用
git push origin <branch-name>
推送自己的修改; - 如果推送失败,则因为远程分支比你的本地更新,需要先用
git pull
试图合并; - 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突后,再用
git push origin <branch-name>
推送就能成功!
如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name>
。
这就是多人协作的工作模式,一旦熟悉了,就非常简单。
小结:
- 查看远程库信息,使用
git remote -v
; - 本地新建的分支如果不推送到远程,对其他人就是不可见的;
- 从本地推送分支,使用
git push origin branch-name
,如果推送失败,先用git pull
抓取远程的新提交; - 在本地创建和远程分支对应的分支,使用
git checkout -b branch-name origin/branch-name
,本地和远程分支的名称最好一致; - 建立本地分支和远程分支的关联,使用
git branch --set-upstream branch-name origin/branch-name
; - 从远程抓取分支,使用
git pull
,如果有冲突,要先处理冲突。
Rebase
一般用于同一分支多人协作时出现提交历史交叉的问题,可以将提交历史变为一条直线
rebase操作的特点:把分叉的提交历史“整理”成一条直线,看上去更直观。缺点是本地的分叉提交已经被修改过了。
标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。
tag就是一个让人容易记住的有意义的名字,它跟某个commit绑在一起。
使用命令git tag <name>
就可以打一个新标签:
git tag v1.0
可以用命令git tag
查看所有标签:
git tag
v1.0
默认标签是打在最新提交的commit上的。
给指定的commit打上标签:
git tag v0.9 f52c633
可以用git show <tagname>
查看标签信息:
git show v0.9
commit f52c63349bc3c1593499807e5c8e972b82c8f286 (tag: v0.9)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:56:54 2018 +0800
add merge
diff --git a/readme.txt b/readme.txt
...
还可以创建带有说明的标签,用-a
指定标签名,-m
指定说明文字:
git tag -a v0.1 -m "version 0.1 released" 1094adb
用命令git show <tagname>
可以看到说明文字:
git show v0.1
tag v0.1
Tagger: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 22:48:43 2018 +0800
version 0.1 released
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (tag: v0.1)
Author: Michael Liao <askxuefeng@gmail.com>
Date: Fri May 18 21:06:15 2018 +0800
append GPL
diff --git a/readme.txt b/readme.txt
...
命令git push origin <tagname>
可以推送一个本地标签;
命令git push origin --tags
可以推送全部未推送过的本地标签;
命令git tag -d <tagname>
可以删除一个本地标签;
命令git push origin :refs/tags/<tagname>
可以删除一个远程标签。
配置别名
# 全局配置
git config --global alias.<name> 'command'
# 本地配置
git config alias.<name> 'command'
# 示例
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.br branch
git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"