Git 学习笔记
廖雪峰老师Git教程真的很不错,花了大概两三个小时过了一遍,受益良多,以记之。
本文不再介绍 Git 的安装配置过程和基本的 linux 命令操作。
初始化仓库
-
首先建一个目录 learngit ,在终端进入到该目录下
-
然后通过
git init
命令把这个目录变成Git可以管理的仓库
$ git init #初始化仓库
把文件添加到版本库
创建一个 readme.txt,放在 learngit 目录下(或者子目录)
$ vim readme.txt
Vim | 快捷键说明 |
---|---|
Esc | 退出编辑,跳到命令模式 |
:w | 保存文件但不退出 vim |
:w! | 强制保存,不退出 vim |
:wq | 保存文件并退出 vim |
:q | 不保存文件,退出 vim |
:q! | 不保存文件,强制退出 vim |
:e! | 放弃所有修改,从上次保存文件开始再编辑 |
1.把文件添加到暂存区
使用git add file_name
$ git add readme.txt
一次添加多个文件
$ git add *.txt
一次添加所有文件
$ git add --all
or
$ git add .
git add --all
和git add .
都可以把「所有改动(增删改)」添加到暂存区,区别是git add .
只会添加当前 **目录(包括子目录)**下的所有改动,git add --all
在任何目录下操作都可以添加当前 工作区 的所有改动。
2.把文件提交到版本库
$ git commit -m "wrote a readme file"
-m "wrote a readme file"
是说明本次 commit 你做了什么事情,简要描述清楚即可(重点是描述清楚,中、英文不限)。
git commit
指定文件的时候会直接提交 工作区 的文件,不指定文件的时候提交的是**缓存区****stage
**的所有文件
$ git commit readme.txt -m "commit message"
查看Git安装目录
$ where git # Windows
$ which git # Mac
时光穿梭机
版本退回
git reset --hard commit_id # 该操作会在`git reflog`中生成记录
-
Git允许我们在版本的历史之间穿梭,使用命令
git reset --hard commit_id
-
Git的
commit_d
(版本号)是一个十六进制的用SHA1计算出来的数字 -
在Git中用
HEAD
表示当前版本,前一个版本就是HEAD^
,前一百个版本写成HEAD~100
-
使用
git reset --hard HEAD^
命令退回上一个版本 -
穿梭前,用
git log
可以查看提交历史,以便确定要回退到哪个版本 -
使用
git log --pretty=oneline
让记录单行显示 -
使用
git log
或git reflog
加file_name
查看指定文件的历史 -
要重返未来,用
git reflog
查看命令历史,以便确定要回到未来的哪个版本 -
Git 提供一个命令
git reflog
来记录你的每一次命令,这样就可以找到所有版本的commit id
工作区和暂存区
- 工作区(Working Directory)
你在电脑里能看到的目录
- 版本库(Repository)
工作区有一个隐藏目录.git
,这个不算工作区,而是Git的版本库.我们可以称它为Repo
Repo里存放了很多东西,其中最重要的就是暂存区stage
(或者叫index),还有Git为我们自动创建的第一个分支(Branch
)master
.以及指向master
的一个指针叫HEAD
.
前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add
把文件添加进去,实际上就是把文件修改添加到暂存区;
第二步是用git commit
提交更改,实际上就是把暂存区的所有内容提交到当前分支。
掌握仓库当前的状态
$ git status
查看修改内容
下面是关于 git diff
的一些使用区别
[图片上传失败...(image-c72280-1547095525316)]
另外可以使用git diff commit_id_1 commit_id_2
比较两个不同版本的区别
撤销修改
-
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令
git checkout -- file_name
-
可以用
git checkout -- *
丢弃所有工作区文件的修改 -
场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步
-
第一步用命令
git reset HEAD file_name
就回到了场景1 -
使用
git reset HEAD
丢弃所有暂存区的修改 -
第二步按场景1操作
-
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,
git reset --hard HEAD
,不过前提是没有推送到远程库。
注意:使用版本退回操作git reset --hard
会导致所有暂存区和工作区的当前修改但未 commit 的内容全部丢弃.。
文件名里有空格和()的情况
Git命令(或者所有的shell命令中)中出现括“()”时系统把它看做一个有特殊意义的命令从而尝试去执行它。
因此如果文件名中出现括号,系统找不到括号里要执行的命令就会报错。
我们要让系统忽略括号的特殊意义,方法是用“”双引号把文件名括起来,或者用转义符将括号转义.
例如文件名为git (1).md
以下的写法都是可以的:
git add "git (1).md"
git add git" "\(1\).md
git add git" (1)".md
删除文件
使用rm file_name
删除本地文件
使用git add file_name
==git rm file_name
提交删除到暂存区
使用git commit -m "commit msg"
提交到本地库
注意: 可以直接使用git rm file_name
删除本地文件以及提交删除到暂存区,但仅用于暂存区有此本地文件的情况
远程仓库
注册GitHub账号
由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置:
- 第1步:创建SSH Key
$ ssh-keygen -t rsa -C "youremail@example.com"
然后在用户主目录里找到.ssh
目录,里面有id_rsa
和id_rsa.pub
两个文件id_rsa
是私钥,id_rsa.pub
是公钥.
cd ~
进入用户主目录
cat ~/.ssh/id_rsa.pub
看到id_rsa.pub文件的内容
- 第2步:登陆GitHub
打开“Account settings”,“SSH Keys”页面:然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容
添加远程库
- 要关联一个远程库,使用命令
git remote add origin git@server_name:path/repo_name.git
这里使用的命令是
git remote add origin git@github.com:HuChanghong/learngit.git
-
关联后,使用命令
git push -u origin master
第一次推送master分支的所有内容 -
此后,每次本地提交后,只要有必要,就可以使用命令
git push origin master
推送最新修改
ssh不行是因为你没有设置ssh秘钥
4: 用ssh -T git@github.com
判断是否绑定成功。如果返回successfully 表示成功
从远程库克隆
- 要克隆一个仓库,首先必须知道仓库的地址,然后使用
git clone
命令克隆。
git clone git@github.com:username/reponame.git
- Git支持多种协议,包括
https
,但通过ssh
支持的原生git
协议速度最快。
取消远程链接
-
查看本地库关联了那些远程库
git remote -v
-
取消本地目录下关联的远程库
git remote rm origin
-
此处
origin
是指你创建与远程库的链接的时候所使用的名字
分支管理
创建与合并分支
-
查看分支:
git branch
-
创建分支:
git branch <name>
-
切换分支:
git checkout <name>
-
创建+切换分支:
git checkout -b <name>
-
合并某分支到当前分支:
git merge <name>
-
删除分支:
git branch -d <name>
解决冲突
-
当Git无法自动合并分支时,就必须首先解决冲突。
-
解决冲突后,再提交,合并完成。
-
解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。
-
用git log --graph命令可以看到分支合并图。
git merge branch_name
提示conflictgit status
查看冲突的文件打开这个文件,修改保存用带参数的git log也可以看到分支的合并情况:git log --graph --pretty=oneline --abbrev-commit
最后删除不需要的分支
分支管理策略
在实际开发中,我们应该按照几个基本原则进行分支管理:首先,
master
分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;那在哪干活呢?干活都在dev
分支上,也就是说,dev
分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev
分支合并到master
上,在master
分支发布1.0版本;你和你的小伙伴们每个人都在dev
分支上干活,每个人都有自己的分支,时不时地往dev
分支上合并就可以了。
因为本次合并要创建一个新的commit,所以加上-m
参数,把commit描述写进去
git merge --no-ff -m "balabala" branch_name
Bug分支
修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;当手头工作没有完成时,先把工作现场
git stash
一下,然后去修复bug,修复后,再git stash pop
,回到工作现场。
Git提供了一个stash
功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作
git stash
我理解的stash
,就像是把当前的WIP
压栈(这里WIP
的含义就是所有修改了但未commit的数据)然后再一个一个取出来,默认的git stash pop
是按照自栈顶开始的顺序依次出栈,也可以使用git stash pop stash@{num}
的方式取出指定的WIP
我们可以用git stash list
查看当前压栈了多少个WIP
注意:git stash
不能将未被追踪的文件(untracked file)压栈,也就是从未被git add
过的文件,也就是你在使用git status
命令看到的提示Untracked files
所列出的文件,所以在git stash
之前一定要用git status
确认没有Untracked files
另外在有WIP
没有commit
或者stash
的情况下是无法切换分支的
Feature分支
-
开发一个新feature,最好新建一个分支;
-
如果要丢弃一个没有被合并过的分支,可以通过
git branch -D <name>
强行删除
多人协作
多人协作的工作模式通常是这样:
-
首先,可以试图用
git push origin <branch-name>
推送自己的修改;* 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull
试图合并;* 如果合并有冲突,则解决冲突,并在本地提交;* 没有冲突或者解决掉冲突后,再用git push origin <branch-name>
推送就能成功!* 如果git pull
提示no tracking information
,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to=origin/<branch-name> <branch-name>
。* 这就是多人协作的工作模式,一旦熟悉了,就非常简单。 -
查看远程库信息,使用
git remote -v
; -
本地新建的分支如果不推送到远程,对其他人就是不可见的;
-
从本地推送分支,使用
git push origin branch-name
,如果推送失败,先用git pull
抓取远程的新提交; -
在本地创建和远程分支对应的分支,使用
git checkout -b branch-name origin/branch-name
,本地和远程分支的名称最好一致; -
建立本地分支和远程分支的关联,使用
git branch --set-upstream-to=origin/branch-name branch-name
; -
从远程抓取分支,使用
git pull
,如果有冲突,要先处理冲突。
rebase
这是git文档强调的使用注意情形和原则
只对尚未推送或分享给别人的本地修改执行
rebase
操作清理历史;从不对已推送至别处的提交执行rebase
操作git rebase
会把你的my work
分支里的每个提交commit
取消掉,并且把它们临时 保存为补丁patch
(这些补丁放到".git/rebase"目录中),然后把mywork
分支更新 到最新的origin
分支,最后把保存的这些补丁应用到mywork
分支上。当mywork
分支更新之后,它会指向这些新创建的提交commit
,而那些老的提交会被丢弃。 如果运行垃圾收集命令pruning garbage collection
, 这些被丢弃的提交就会删除.
当
mywork
分支更新之后,它会指向这些新创建的提交commit
,而那些老的提交会被丢弃。 如果运行垃圾收集命令pruning garbage collection
, 这些被丢弃的提交就会删除.
标签管理
发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本。将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来。所以,标签也是版本库的一个快照。Git的标签虽然是版本库的快照,但其实它就是指向某个commit的指针(跟分支很像对不对?但是分支可以移动,标签不能移动),所以,创建和删除标签都是瞬间完成的。
类似IP和域名的关系
创建标签
-
命令
git tag <tagname>
用于新建一个标签,默认为HEAD
,也可以指定一个commit id
; -
git tag tag_name commit_id
-
命令
git tag -a <tagname> -m "blablabla..."
可以指定标签信息; -
用
-a
指定标签名,-m
指定说明文字 -
命令
git tag
可以查看所有标签。 -
git show tag_name
查看标签信息
操作标签
命令git push origin <tagname>
可以推送一个本地标签;命令git push origin --tags
可以推送全部未推送过的本地标签;命令git tag -d <tagname>
可以删除一个本地标签;命令git push origin :refs/tags/<tagname>
可以删除一个远程标签。
关联多个远程库
使用多个远程库时,我们要注意,
git
给远程库起的默认名称是origin
,如果有多个远程库,我们需要用不同的名称来标识不同的远程库。
如果本地库已经关联了origin
的远程库,并且,该远程库指向GitHub
。我们可以删除已有的GitHub
远程库
git remote rm origin
使用如下命令关联远程库
git remote add remote_name git@server_name:path/repo_name.git
例如要关联Github
远程库
git remote add github git@github.com:Github_id/repo_name.git
再比如要关联码云
远程库
git remote add gitee git@gitee.com:Github_id/repo_name.git
此时要推送到不同的远程库则代码中使用对应的远程库名称(remote_name
)
git push remote_name master
自定义Git
忽略特殊文件
忽略文件的原则是:
-
忽略操作系统自动生成的文件,比如缩略图等;
-
忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;
-
忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。
可以用git add -f
强制添加被gitignore
的文件
可以用git check-ignore
命令检查是什么规则导致的无法添加文件
git check-ignore -v file_name
注意:.gitignore
文件本身要放到版本库里,并且可以对.gitignore
做版本管理!
配置别名
st = status
co = checkout
br = branch
cm = commit
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的时候,加上--global
是针对当前用户起作用的,如果不加,那只针对当前的仓库起作用。每个仓库的Git配置文件都放在.git/config
文件,打开配置文件,别名就在[alias]
后面,要删除别名,直接把对应的行删掉即可当前用户的Git配置文件放在用户主目录
下的一个隐藏文件.gitconfig
中
查看用户的配置信息使用git config --global --list
命令
查看当前仓库的配置信息使用git config --local --list
命令
删除别名
命令删除一个已定义的别名 git config --global --unset alias.ci
cd .git
vim config
打开对应的配置文件,别名就在[alias]
后面,直接把对应的行删掉即可
gitconfig配置文件的读取顺序:
linux中类似用户的配置文件一样有3层,系统级,用户级,项目级。
windows也基本一样,但常常只存在于是用户根目录(C:\User\xxx),项目目录中。 最下层的覆盖上面的,alias配置也在其中,手动改配置文件也和命令一样。 加了--global选项的,表示配置写到了用户级,--system是系统级,win是在安装目录(如C:\Program Files\Git\mingw64\etc),不加就在项目级