已经开始工作20多天了,每天都面对一堆没见过不会做的东西,想要努力学,也总觉得时间不够。每次领导找我说话,我都担心会让我明天不要来上班了。其中,让我觉得最坑的就是Git。
之前也使用过git,但都是自己的小项目,不涉及到多人协作,也没什么分支的概念,所以只用master,自己push来pull去的,也没出过什么问题。但是工作之后设计到多个任务同时推进,所以需要频繁切换分支,提交给老师们review,于是闹出了许多提交代码不干净的尴尬笑话。
今天认真看了git pro,也就是git自己的官方书,类似文档。看了前面三张操作部分的内容,下周再来看原理。
Git Pro
Git 如何工作
版本控制的工具
- 分布式版本控制系统
- 客户端把代码仓库完整地镜像下来,如果一处协同工作的服务器发生故障,事后可以用任何一个镜像除了的本地仓库恢复
- 每一次克隆操作,实际上都是一次对代码仓库的完整备份
原理
- 直接记录快照
git VS 其他版本控制系统
其他版本控制系统记录以文件列表的方式存储信息,将保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异
git在每次提交时,对全部文件制作一个快照,并保存这个快照的索引,如果文件没有修改,就保留一个指向之前文件的索引
- 所有操作都在本地执行
- 执行速度快
- 执行历史在本地保存,清晰
- 无网络连接时,不影响工作
- 所有数据在存储钱都计算校验和,以校验和来引用
- 如果传送过程中丢失信息或损坏文件,git会发现
- 也不会再Git不知情时更改任何文件内容或目录内容
- 计算校验和的机制叫做SHA-1散列
- 一般只添加数据
- 很难执行不可逆操作或者清除数据
三种状态
- 已提交(commited): 数据已安全地保存在本地数据库中
- 已修改(modified):修改了文件,但还没有被保存到数据库中
- 已暂存(staged):对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中
三个工作区域
- git仓库(.git directory / repository):git用来保存项目的元数据和对象数据库的地方
- 工作目录(working directory):对项目的某个版本独立提取出来的内容
- 暂存区域(staging area):是一个文件,保存了下次要提交的文件列表信息,一般在git仓库目录中
git 基本工作流
- 在工作目录中修改文件
- 暂存文件,将文件的快照放入暂存区域
- 提交更新,找到暂存区域的文件,将快照永久性存储到git仓库目录
Git 如何操作
配置git config
存储位置
/etc/gitconfig
:包含系统上每一个用户及他们仓库的通用配置~/.gitconfig
或~/.config/git/config
: 针对当前用户,可以传递--global
选项让Git读写此文件- 当前使用仓库的git目录中的
config
文件:针对该仓库
用户信息
设置用户名称和邮件地址,每一个git提交都会使用这些信息,并且它会写入到每一次提交中,不可更改
1 | git config --global user.name "xxx" |
- 如果使用了
--global
选项,那么该命令只需要运行一次 - 如果想在当前项目中使用不同的用户名称和邮件地址,可以在当前项目目录下运行没有
--global
选项的命令来配置
文本编辑器
配置默认的文本编辑器
1 | git config --global core.editor emacs |
检查配置
1 | git config --list |
- 可能会看到重复的变量名,因为git会从不同的文件中读取同一个配置,这种情况下,git会找到它找到的每一个变量的最后一个配置
- 也可以通过输入
git config <key>
找检查某一项配置
获取帮助
需要获取帮助时,有三种方法可以找到git命令的使用手册
1 | git help <verb> |
基本使用
获取git仓库
1. 在现有目录中初始化仓库
1 | git init |
该命令创建一个名为
.git
的子目录如果需要添加文件来进行版本控制
1
2
3git add *.js
git add LICENSE
git commit -m "initial project version"
2. 克隆现有仓库
默认配置下远程git仓库中的每一个文件的每一个版本都将被拉取下来
1
git clone https://github.com/xxx/xxx
如果想自定义本地仓库的名字,可以使用如下命令
1
git clone https://github.com/xxx/xxx mulibgit
记录每次更新
- 每一个文件都只有两种状态
- 已跟踪:被纳入了版本控制的文件,在上一次快照中有它们的记录
- 未跟踪:既不存在于上次快照的记录中,也没有放入暂存区
- 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态
检查当前文件状态
git status
查看哪些文件处于什么状态,显示内容包:- 所有文件是否都被跟踪
- 文件是否有修改
- 当前分支
跟踪新文件
1 | git add <file> |
暂存已修改文件
1 | git add <file> |
git add
可以理解成「添加内容到下一次提交中」
提示语
changes to be commited
- 文件是已暂存状态,如果此时提交,那么该文件此时的版本将被留存在历史记录中
- 如果参数是目录的路径,该命令将递归地跟踪该目录下的所有文件
changes not staged for commit
- 已跟踪文件的内容发生了变化,但还没有放到暂存区
- 要暂存这次更新,需要运行
git add
命令
状态简览
git status -s
/git status --short
可以更为紧凑地输出状态- 文件标记
??
:未跟踪文件A
:新增加到暂存区的文件- 出现在右边的
M
:表示该文件被修改了,但是还没放入暂存区 - 出现在左边的
M
:文件被修改了,并放入了暂存区
忽略文件
.gitignore
文件- 无需纳入git管理的文件,通常是一些自动生成的文件
- 要养成一开始就设置好
.gitignore
文件的习惯,以免将来误提交这类无用的文件
查看已暂存和未暂存的修改
git diff
: 比较工作目录中当前文件和暂存区域快照直接的差异,也就是修改之后还没有暂存起来的变化内容git diff --cached
/git diff --staged
:查看已暂存的将要添加到下次提交里的内容
提交更新
正确顺序:
git status
查看文件是否都暂存起来了git commit
提交
git commit -m "xxx"
提交+提交内容的信息
跳过使用暂存区域
git commit -a
自动把所有已经跟踪过的文件暂存起来一并提交,跳过git add
步骤
移除文件
git rm
从已跟踪文件清单中移除制定的文件,下一次提交时,该文件就不再纳入版本管理了git rm xxx -f
删除之前修改过并且已经放到暂存区的文件,这样的数据不能被git恢复git rm --cached xxx
把文件从git仓库中删除,但仍保留在当前的工作目录中
移动文件
git mv file_from file_to
将文件从一个文件夹移动到另一个文件夹
git mv README.md README
实际执行的是:
mv README.md README
git rm README.md
git add README
查看提交历史
git log
会按提交时间列出所有的更新,最新的更新排在最上面,内容包括- 每次提交的SHA-1校验和
- 作者名字
- 电子邮件地址
- 提交时间
- 提交说明
git log -n
最近n次提交git log -p
显示每次提交的内容差异git log --stat
查看每次提交的简略的统计信息git log --pretty
可以指定使用不同于默认格式的方式展示提交历史git log --pretty=oneline
每个提交显示一行git log --pretty=short
有commit信息git log --pretty=full
详细信息,包括author,commitgit log --pretty=fuller
详细信息,包括具体author,authordate,commit,commitdategit log --pretty=format: "xxx"
定制要显示的记录格式(可以自己规定中间的分隔符)
git log --graph
图形显示分支、合并历史
限制输出长度
git log -<n>
只输出n条提交git log --since=2.weeks
最近两周的提交- 可以是
weeks
,years
,months
- 可以是具体的某一个天
2008-01-15
- 可以是相对多久以前
2years 1day 3minutes ago
- 可以是
git log --until=
截止到什么时候git log --author
指定作者的提交git log --grep
搜索提交说明中的关键字git log --all-match
同时满足两个选项搜索条件
git log -S
列出添加或溢出了某些字符串的提交git log path
只关心某些文件或者目录的历史提交
撤销操作
git commit --amend
重新提交上一次提交(在漏掉文件或提交信息写错的情况下使用)- 会将暂存区的文件提交,如果上次提交后未做修改,那么快照不变,只修改提交信息
取消暂存的文件:
git reset HEAD <file>
取消对文件的修改:
git checkout -- <file>
远程仓库的使用
查看远程仓库
git remote
: 列出你指定的每一个远程服务器的简写git remote -v
远程服务器简写 + 对于的URL
添加远程仓库
git remote add <shrotname> <url>
git fetch xx
拉取远程仓库中有单你没有的信息
从远程仓库抓取和拉取
git fetch [remote-name]
- 访问远程仓库,从中拉取所有你还没有的数据
- 执行完后,你会永远那个远程仓库中所有分支的引用,可以随时合并或查看
- 它不会自动合并或修改你当前的工作,需要手动将其合并入你的工作
git pull
- 有一个分支设置为跟踪一个远程分支,可以使用
git pull
来自动抓鱼然后合并远程分支到当前分支
- 有一个分支设置为跟踪一个远程分支,可以使用
推送到远程仓库
git push [remote-name] [branch-name]
将内容推送到上游- 多人合作时,需要先将他人的工作拉取下来,合并进你的工作,然后才能推送
查看远程仓库
git remote show [remote-name]
查看某一个远程仓库的更多信息
远程仓库的移除与重命名
git remote rename
修改远程仓库的简写名git remote rm xxx
移除一个远程仓库
打标签
人们会使用这个功能来标记发布节点
列出标签
git tag
git tag -l 'v1.8.5*'
列出1.8.5系列的标签
创建标签
- 两种类型
- 轻量标签(lightweight):特定提交的引用(像一个不会改变的分支)
- 附注标签(annotated):存储在git数据库汇总的一个完整对象
附注标签
git tag -a v1.4 -m "my version 1.4"
git show v1.4
显示标签信息和对应的提交信息
轻量标签
- 本质上是将提交校验和存储到到一个文件中,没有保存任何其他是信息
git tag v1.4-lw
git show v1.4-lw
不会显示额外的标签信息,只显示提交信息
后期打标签
git tag -a v1.2 xxxxxx
后面接校验和
共享标签
- 默认情况下,
git push
不会传送标签到远程仓库服务器上 - 所以必须显式地推送标签到共享服务器上
git push origin [tagname]
git push origin --tags
会把所有不再远程仓库服务器上的标签全部传送过去
检出标签
git checkout -b [branchname] v2.0.0
在特定标签上创建一个新分支
git别名
- 可以通过
git config
来为每一个命令设置一个别名
Git分支
- 分支本质上仅仅是指向提交对象的可变指针
基础操作
分支创建
git branch <branchname>
创建分支git log --decorate
查看各个分支当前所指的对象
分支切换
git checkout <branchname>
切换分支(HEAD
会指向切换的分支)
分支的新建与合并
git checkout -b <branchname>
创建并切换到新分支git merge <branchname>
将其他分支的内容合并到当前分支上git branch -d <branchname>
删除分支
正确工作流
做任务A时,创建A分支 (当前分支
master
)1
git checkout -b A
需要执行任务B,先保存任务A的修改,再从master上创建新分支B
1
2
3git stash
git checkout master
git checkout -b B在B上完成任务后,合并分支B到master,再删除分支B
1
2
3git checkout master
git merge B
git branch -d B
遇到冲突时的分支合并
git status
可以在任何时候使用这一命令来查看因包含合并冲突而处于未合并状态的文件解决方案
- 手动解决
- mergetool
分支管理
git branch -v
查看所有分支的最后一次提交git branch --merged
/git branch --no-merged
查看哪些分支已经合并及未合并
分支的开发工作流
- 不同分支可以维护不同层次的稳定性
- 对于大型项目来说,稳定分支(master)通常会在提交历史中落后一大截
- 分类
- 特性分支
- 短期分支
- 用来实现单一特性或其相关工作
- 特性分支
远程分支
- 远程引用是对远程仓库的引用(指针)
git ls-remote
获取远程引用的完整列表- 远程跟踪分支: 远程分支状态的引用
- 本地引用不能移动
- 在做网络通信操作时,它们会自动移动
- 推送
git push origin serverfix
后一个分支即refs/heads/serverfix:refs/heads/serverfix
- 推送本地的
serverfix
分支来更新远程仓库上的serverfix
分支
- 推送本地的
- 跟踪分支:从一个远程跟踪分支检出一个本地分支会自动创建一个叫做“跟踪分支”
git checkout -track [remotename]/[branch]
设置跟踪分支git branch -vv
查看设置的所有跟踪分支
变基
变基的基本操作
- 变基:提取在B分支引入的补丁和修改,在A分支上再应用一次
rebase
:将提交到某一分支上的所有修改都移至另一分支上
原理
- 找到A、B分支最近的共同祖先
- 对比当前分支(B)的历次提交,提取相应的修改保存为临时文件
- 将该分支指向目标基底A,将另存为临时文件的修改依序应用
变基 VS 合并
将dev分支的内容并入到master中
在master分支上执行
git merge dev
在dev分支上依次执行以下命令
1
2
3git rebase master
git checkout master
git merge experiment
以上两种方法的结果是一样的,只是变基会使提交历史看起来更整洁
不要对在你的仓库外有副本的分支进行变基