版本控制
集中式(svn)
- svn 因为每次存的都是差异,需要的硬盘空间会相对的小一点,但回滚的速度会很慢
- 优点: 代码存放在单一的服务器上 便于项目的管理
- 缺点: 服务器宕机,数据就存在丢失的风险
分布式(git)
- git 每次存的都是项目的完整快照,需要的硬盘空间会相对大一点
(Git 团队对代码做了极致的压缩,最终需要的实际空间比 svn 多一点,但回滚速度极快) - 优点: 完全的分布式
- 缺点: 学习起来比 SVN 陡峭
- git 每次存的都是项目的完整快照,需要的硬盘空间会相对大一点
底层命令
git 对象
- 生成一个 hash 值:压缩后的文件内容的键值对存到.git/objects:
1
git hash-object -w fileUrl
tree 对象
- 往暂存区添加一条记录(让 git 对象对应上文件名)存到.git/index:
1
git update-index --add --cacheinfo 100644 hash test.txt
- 生成树对象存到.git/objects:
1
git write-tree
commit 对象
- 生成一个提交对象存到.git/objects:
1
echo 'first commit' | git commit-tree treehash
初始化配置和文件的 UDR 命令
初始化配置
- 配置用户名:
1
git config --global user.name "name"
- 配置邮箱:
1
git config --global user.email name@example.com
- 获取设置配置列表:
1
git config --list
- 初始化仓库: git init
U(修改提交操作)
- 将单个文件从工作区添加到暂存区: git add file
- 将所有文件添加到暂存区: git add .
- 将暂存区的文件提交到本地仓库: git commit -m “msg”
D(删除 & 重命名)
- 删除:git rm deleteFileName
- 重命名:git mv oldFileName newFileName
R(查询操作)
- 查看工作目录中文件的状态: git status(已跟踪(已提交 已暂存 已修改) 未跟踪)
- 查看未暂存的修改,比较文件不同,即暂存区和工作区的差异: git diff
- 查看未提交的暂存:
1
git diff --cache
- 查看提交记录:
1
git log --oneline
分支
分支的本质其实就是一个提交对象!!!
- HEAD:
- 一个指针,它默认指向 master 分支,切换分支时其实就是让 HEAD 指向不同的分支
- 每次有新的提交时 HEAD 都会带着当前指向的分支,一起往前移动
git 分支命令
- 查看整个项目的分支图:
1
git log [--oneline | --decorate | --graph | --all]
查看分支
- 查看分支列表: git branch
- 查看所有分支: git branch -a
- 查看分支指向的最新的提交: git branch -v
- 查看合并到当前分支的分支列表:
1
git branch --merged // 一旦出现在这个列表中就应该考虑删除已合并的分支
- 查看没有合并到当前分支的分支列表:
1
git branch --no-merged // 一旦出现在这个列表中就应该观察一下是否需要合并
创建分支
- ⭐ 创建分支: git branch createName
- 在指定的提交对象上创建新的分支: git branch createName commithash — 版本穿梭(时光机)
删除分支
- ⭐ 删除本地分支: git branch -d deleteFeatureName
- 强制删除分支: git branch -D deleteFeatureName
- ⭐ 删除远程分支: git push origin -d deleteOriginFeatureName
切换分支
- ⭐ 切换分支: git checkout featureName
- 创建并切换到新分支: git checkout -b featureName
合并分支
- 合并某个分支到当前分支,默认 fast forward: git merge featureName
- 快进合并 –> 不会产生冲突
- 典型合并 –> 有机会产生冲突
- 解决冲突 –> 打开冲突的文件 进行修改 add commit
git 分支的注意点
- 在切换的时候 一定要保证当前分支是干净的!!!
- 『允许』切换分支:
- 分支上所有的内容处于『已提交状态』
- (避免)分支上的内容是初始化创建 处于未跟踪状态
- (避免)分支上的内容是初始化创建 第一次处于已暂存状态
- 『不允许』切分支:
- 分支上所有的内容处于『已修改状态』或第二次以后的『已暂存状态』
- 在分支上的工作做到一半时 如果有切换分支的需求, 我们应该将现有的工作『贮藏』起来
贮藏
- ⭐ 将当前工作区改过的文件贮藏到栈中,贮藏后当前库是上一个已提交的状态: git stash
- ⭐ 查看当前贮藏列表的文件:git stash list
- 将栈顶的工作内容恢复,但不让任何内容出栈:
1
2git stash apply <name>
例: git stash apply stash@{1} - 取出栈顶的工作内容后就应该将其删除(出栈): git stash drop
- ⭐ 恢复贮藏起来的文件并把列表中对应文件删除,是 apply+drop 操作:git stash pop fileName
- 删除所有stash记录:git stash clear
- 保存当前未commit代码并添加备注:
1
git stash save "备注的内容"
撤销与回退操作(后悔药)
撤销:当修改了工作区/暂存区的文件,想要撤销之前的操作
场景 1:(撤销工作目录的修改)当改乱了工作区某个文件的内容,但还未 add 到暂存区
- 撤销指定文件的修改:
1
git checkout -- fileName // -- 表示是文件名不是分支名,不混淆可不用
- 撤销所有工作区的修改:git checkout .
- 撤销工作区和暂存区的所有修改:git checkout -f
- 撤销指定文件的修改:
场景 2:(撤销暂存区的修改)当乱改了工作区某个文件的内容,并且 git add 到了暂存区
- 将暂存区的文件修改撤销掉:git reset HEAD fileName
- 将工作区的文件修改撤销掉:git checkout fileName
场景 3:乱改了很多文件,想回到最新一次提交时的状态
- 撤销工作区中所有未提交文件的修改内容:
1
git reset --hard HEAD
- 撤销工作区中所有未提交文件的修改内容:
场景 4:(撤销提交)在当前最后一次提交的 commit 信息,还没有 push 到远程
1
git commit --amend
回退:当已经进行了 commit 操作,需要回退到之前的版本:
- 查看每一步操作的记录: git reflog (谨慎!!!每次改变都会记录)
- 设置为某一 id 的记录: git reset id
- 设置为对应 id 记录:
1
git reset --hard id
- 回退到上次提交的状态:
1
git reset --hard HEAD^
- 回退到 n 个版本前的状态:
1
git reset --hard HEAD~n
- 回退到某一个 commitid 的状态及内容,只改变 HEAD:
1
git reset --soft HEAD commitid(会把commitid之后的记录放到暂存区)
- 回退到某一个 commitid 的状态,并保留工作区的内容:
1
git reset --mixed(默认) HEAD commitid
- 回退到某一个 commitid 的状态及内容,并重置暂存区和工作目录:
1
git reset --hard HEAD commitid
reflog
- 回滚操作:适用于reset命令后发现误操作了其他记录
1
2查看历史记录: git reflog // 把错误提交的commitHash记住
回滚错误: git reset --hard commitHash
checkout 深入
1 | git checkout brancname 跟 git reset --hard commithash 特别像 |
共同点
- 都需要重置 HEAD 暂存区 工作目录
区别
- checkout 对工作目录是安全的
reset --hard
是强制覆盖 - checkout 动 HEAD 时不会带着分支走而是切换分支
reset --hard
时是带着分支走
checkout + 路径
1 | git checkout commithash filename |
- 重置暂存区
- 重置工作目录
1 | git checkout -- filename |
- 重置工作目录
远程协作
基本流程
项目经理初始化远程仓库
- 一定要初始化一个空的仓库; 在 github 上操作
项目经理创建本地仓库
1
2
3
4
5git remote 别名 仓库地址(https)
git init 将源码复制进来
修改用户名 修改邮箱
git add
git commit项目经理推送本地仓库到远程仓库
1
git push 别名 分支 // 输入用户名密码;推完之后会附带生成远程跟踪分支
项目邀请成员 & 成员接受邀请
- 在 github 上操作
成员克隆远程仓库
1
git clone 仓库地址 // 在本地生成.git 文件 默认为远程仓库配了别名 orgin
- 只有在克隆的时候 本地分支 master 和 远程跟踪分支别名/master 是有同步关系的
成员做出贡献
- 修改源码文件
1
2
3git add .
git commit
git push 别名 分支 // 输入用户名 密码;推完之后会附带生成远程跟踪分支
- 修改源码文件
项目经理更新修改
1
2git fetch 别名 // 将修改同步到远程跟踪分支上
git merge 远程跟踪分支
做远程跟踪
- 克隆仓库时会自动为 master 做跟踪
- 本地没有分支:
1
git checkout --track remote/featureName
- 本地已经创建了分支:
1
git branch -u remote/featureName
远程操作
- 刷新远程分支信息: git fetch
- 将本地 master 分支推送到远程对应分支: git push origin master
- 下载远程代码并合并: git pull
- 拉取远程最新代码并合并,不会产生 Merge:
1
git pull --rebase
- 推送本地代码到远程分支上: git push
- 将远程仓库和本地仓库关联起来: git remote add origin url
- 查看远程库信息: git remote -v
rebase 和 merge
rebase: 把分叉的提交历史“整理”成一条直线,看上去更直观
- 变基:git rebase
- 将当前分支移植到指定分支或者 commit 之上:git rebase -i commit
- 从远程回退到未提交状态:git reset HEAD^
merge: 每次操作都会产生一条 merge 记录,从日志上看的,会产生分叉,日志看起来杂乱
git 修改默认 pull 的参数
- git pull = git fetch + git merge
- 全局修改 pull 的命令:
1
git config --global --add pull.rebase true
- 查看命令是否修改成功:
1
git config --global -l
- 如果之前向全局配置文件修改过 pull 的配置项,但修改错了,使用
--unset
- 使用–unset 后,发现提示有多个值,则使用
--replace-all
1
git config --global --replace-all pull.rebase true
修改全局配置项
新增一个配置项:--add
1 | 格式:git config --local/--global/--system --add section.key value |
获取一个配置项:--get
1 | 格式:git config --local/--global/--system --get section.key |
删除一个配置项:--unset
1 | 格式:git config --local/--global/--system --unset section.key |
查看配置文件:--list/-l
- 查看仓库级的 config:
1
git config --local -l
- 查看全局级的 config:
1
git config --global -l
- 查看系统级的 config:
1
git config --system -l
- 查看当前生效的配置:git config -l
编辑配置文件:--edit/-e
- 编辑仓库级的 config:
1
git config --local -e
- 编辑全局级的 config:
1
git config --global -e
- 编辑系统级的 config:
1
git config --system -e
基本概念
- 本地仓库:
- 存放所有相关的文件,具体分为工作区、暂存区和仓库区,『暂存区和仓库区则在.git文件夹下』
- 工作区:
- 即我们工作的文件夹(不包含.git文件夹),在里面进行文件的增删改操作
- 暂存区:
- 临时保存工作区的改动,通过git add操作将工作区的修改同步到暂存区
- 仓库区:
- 当执行git commit操作时,将暂存区上的所有变动同步到『本地仓库』
- 远程仓库:
- GitHub/GitLab 上保存的仓库,通过 git push 将本地仓库同步到远程仓库,也可以通过 git fetch/pull 将远程仓库同步到本地仓库
- 快照:
- git 将顶级目录中的文件和文件夹称作集合,并通过一系列快照来管理历史记录。在 git 的术语中,
- 『文件被称为blob对象(数据对象)』,也就是一组数据。
- 『目录则被称为tree(树)』,目录可以包含文件和子目录。
- 『快照统称“commit”』,每个快照都有一系列的父辈。『每次commit都是创建一个全新的记录』
- master 引用通常会指向『主分支最新一次 commit』.在 Git 中,『当前的位置』有一个特殊的索引,它就是“HEAD”。
checkout 可以用于切换分支,可以从历史提交或暂存区中拷贝文件到工作目录
- 当给定某个文件名时,git 会从指定的提交中拷贝文件到暂存区域和工作目录。
HEAD 标识处于分离状态的提交操作
- 可以正常提交,但不会更新任何命名分支,一旦切换到别的分支,这个提交节点再也不会被引用就会被丢弃
- 如果想保存这个状态,可以用 git checkout -b name 来创建一个新的分支
Reset
- 也用来从仓库中复制文件到索引,而不动工作目录
- 当前分支默认指向到那个提交。用
--hard
,工作目录也更新,用--soft
,都不变 - 没有给出提交点的版本号,默认用 HEAD。分支执行不变,但是索引会回滚到最后一次提交。用
--hard
,工作目录也同样
revert
- 将现有的提交还原,恢复提交的内容,并生成一条还原记录。
- git revert commitHash
cherry-pick
- 将已经提交的 commit,复制出新的 commit 应用到分支里
- 应用场景: 比如全部都在master上开发,突然当前a开发没开发完或先不上,要先开发b,那么a不需要需要移到其他分支,避免污染代码记录
- 获取commitHash:git log
- 复制单个: 先复制对应记录的commitHash,然后切到其他分支:git cherry-pick commitHash
- 复制多个:git cherry-pick commitHash1 commitHash2
- 复制多个连续的commit:git cherry-pick commit1^..commit2
- 遇到冲突:先解决冲突,在执行
1
git cherry-pick [--continue/放弃 --abort(回到之前)/退出 --quit(保留操作成功的)]
Merge
- merge 命令把不同分支合并起来。『合并前,索引必须和当前提交相同。』
- cherry-pick 复制一个提交节点并在当前分支做一次完全一样的新提交
Rebase
- 合并分支。本质上,是线性化的自动的 cherry-pick
工作原理
1 | graph TB |
- 查看每个 SHA-1 的类型: git cat-file -t
- 查看每个对象的内容和简单的数据结构: git cat-file -p
- 把当前文件放入暂存区: git add files
- 把暂存区生成快照并提交本地仓库: git commit
- ⭐ 用来撤销暂存区的某一文件, git reset 撤销所有暂存区的文件:
1
git reset -- files
- 把文件从暂存区复制到仓库区,用来丢弃本地修改:
1
git checkout -- files
- 回滚到复制最后一次提交:
1
git checkout HEAD -- files
- 相当于运行 git add 把工作区的所有文件加入暂存区再提交到仓库区: git commit -a
- 进行最后一次提交+工作目录中文件快照的提交,并且文件被添加到暂存区:git commit files
- 比较两次提交记录的差异:git diff commitId commitId
- 比较跟远程分支的差异: git diff HEAD
- 比较跟暂存区的记录的差异: git diff
- 比较跟某一分支记录的差异: git diff featureName
- 使用与当前提交相同的父节点进行一次新提交,旧的提交会被取消:
1
git commit --amend