Git 经验总结
简介
Git
是一种分布式版本控制系统,它可以不受网络连接的限制,加上其它众多优点,目前已经成为程序开发人员做项目版本管理时的首选,非开发人员也可以用 Git
来做自己的文档版本管理工具。
Git
本地仓库结构如下:
Git 常用操作
创建本地仓库和链接 GitHub 远程仓库
IDEA 自带的 Git 管理其实可以比较方便地完成这些操作。
创建本地仓库:git init
克隆一个远程仓库到本地:git clone [url]
将远程仓库连接到本地仓库:git remote add origin <REMOTE_URL>
配置开发者信息:git config user.name [name]
查看状态:git status -s
以上这些配置在 IDEA 里面都有更方便的方法。
操作文件
Command | 效果 |
---|---|
git add README.md | 添加单一文件到本地暂存区 |
git add -A | 添加工作区所有不在 .gitignore 列表里的文件到暂存区 |
git add -p | 交互命令中最有用的 patch 模式 |
git add -u | 进入交互命令中的 update 模式 |
git add --ignore-removal . | 添加工作区 修改 或 新增 的文件列表, 删除 的文件不会被添加 |
**patch 模式:**交互命令中最有用的模式,其操作方式和 update
类似,选择后 Git
会显示这些文件的当前内容与本地版本库中的差异,然后可以自己决定是否添加这些修改到暂存区,在命令行 Stage deletion [y,n,q,a,d,/,?]?
后输入 y,n,q,a,d,/,?
其中一项选择操作方式,具体功能解释如下:
- y:接受修改
- n:忽略修改
- q:退出当前命令
- a:添加修改
- d:放弃修改
- /:通过正则表达式匹配修改内容
- ?:查看帮助信息
**update 模式:**它会先列出工作区 修改
或 删除
的文件列表,新增
的文件不会被显示,在命令行 Update>>
后输入相应的列表序列号表示选中该项,回车继续选择,如果已选好,直接回车回到命令主界面
建议:在 IDEA 中,git add/mv/rm 的所有操作基本都可以被 IDEA 自动托管,并且能够在自动保存时自动 update,同时在编辑时可视化显示所有的更改,所以能用 IDEA 就用 IDEA 吧。
Commit 操作
提交自己的更新到本地版本库,作为一个新的版本
基本操作:git commit -m '提交原因/message'
提交信息中需要包含自己这个版本相较于上一个版本所做的改动,这个 commit 还包含一个 SHA 和作者信息(例如姓名和邮箱这些在 config 中配置好的信息)。
git commit --amend
用于修改上次的 commit,一般是存在一些小的 bug 需要小改,但是有没必要生成一个新的 commit(太多 commit 会带来版本管理的负担),所以采用 --amend 模式直接将小修改融入到上次一的 commit 里面。
Branch 操作
通过 git branch -a
查看本地和远程仓库上的所有分支。
Command | 效果 |
---|---|
git branch daily/0.0.0 | 创建分支 |
git branch -m daily/0.0.0 daily/0.0.1 | 重命名分支 |
git branch -d daily/0.0.1 | 删除分支 |
基本没有什么可以说的,提交的操作记得注意分支
Checkout
切换操作,可以切换到指定的分支和提交上,通过 checkout 在指定 commit 创建分支并且融合可以有效保证 git 版本管理的线性和干净。
示例:
git checkout 84ae6cd # 选择到历史 commit
git checkout -b temp # 在历史 commit 上创建新分支 temp,并选择该分支
git fetch origin featureA:temp # 把服务器上的 featureA 分支的最新内容都拉取到 temp 上来,此时拉取下来的内容会保留在 FETCH_HEAD 类似临时分支的地方
git merge FETCH_HEAD # 合并
参考:https://stackoverflow.com/questions/9237348/what-does-fetch-head-in-git-mean
Merge 操作
merge 操作是将两个分支的历史记录合并到一个新的提交中。当你执行merge操作时,Git会自动找到两个分支的最近共同祖先节点,然后比较这些节点之间的差异,将这些差异合并到一个新的提交中。这个新提交会包含两个分支的所有变更内容,并成为这两个分支的最新公共祖先。
Git merge有两种主要的合并策略:fast-forward
和 recursive
。
-
在
fast-forward
合并策略中,如果一个分支的提交历史是另一个分支的子集,那么Git会简单地将当前分支指向另一个分支的最新提交,这样就完成了快速合并操作。 -
在
recursive
合并策略中,Git会创建一个新的提交来表示合并结果,这个提交会包含两个分支的所有变更内容。
Merge 常用指令如下;
--no-ff
:禁用fast-forward合并策略,强制Git创建一个新的合并提交。保留被合并分支的外分历史。--squash
:将合并结果压缩为一个提交,并且不会保留源分支(source branch,其实就是被合并的分支)的提交历史。相当于把被合并的分支的所有改动都合并成一个新的 commit 提交到主分支上去了,对于一人一分支的开发模式,这样可以保证共同开发分支的提交历史的简洁。-m <message>
:指定新的合并提交的提交信息。
Pull/Push 和远端服务器操作
由于 git 的分布式特性,每个人都在自己的本地仓库开发,并将所有贡献提交给一个远端代码仓库进行集中整合,同时也需要即使获取远端仓库里面其他人的提交更新,用于统一进度,以上对应就是 push 和 pull 操作。
git pull
git pull origin
默认拉取 master 分支到当前分支,实际上等价于 git fetch + git merge
,用于同步远端仓库的进度并且提前解决自己的本地 commit 推送到远端带来的冲突问题,由于存在 merge 操作,所以会存在版本历史的分支链路不够线性和整洁的问题,解决方案下面再说。
git push
默认情况下,git push会推送暂存区所有提交(也即HEAD及其之前的提交),使用下面的命令可以改变此默认行为:
git push <remotename> <commit SHA>:<remotebranchname>
远程仓库名,默认为origin
提交的唯一码
远程分支名
上面的命令会将暂存区内 代表的提交以及其之前尚未推送到远程的提交一起提交到远程。
push 会因为冲突导致 reject, 这个时候就需要 pull 然后在本地解决冲突再 push 上传。
进阶操作 rebase
Git 由于分布式的特性,在多人合作开发时往往会出现相互冲突的问题,这个时候就需要 Merge 操作来解决冲突,理想上这特别美好,但如果同一个分支上面大家每隔几个 commit 都有一个冲突需要 Merge 一次,这种混乱的分支链路的和一堆无用的合并对 Git Commit 历史追踪是灾难性的。
实际上,理想的状态是,当多人对同一个分支进行开发时,自己的 Commit 最好能跟在远端仓库最新的 Commit 之后,而不是粗暴地去做 Merge 合并,保证这个开发链路的干净整洁。
rebase
字面意思,更改基底,如果当前分支为 feature
,执行 git rebase master
则 Git 会执行下面操作:
分支历史示意图:
D---E feature
/
A---B---C master
- 找到这两个分支(即当前分支
Feature
、rebase
操作的目标基底分支Master
)的最近共同祖先提交 A - 然后对比当前分支相对于该祖先提交的历次提交(D 和 E),提取相应的修改并存为临时文件,然后将当前分支指向目标基底
Master
所指向的提交 C, 最后以此作为新的基端将之前另存为临时文件的修改依序应用。
完成之后分支历史如图:
* 74199ce (HEAD -> master, feature) commit F
* e7c7111 commit E
* d9623b0 commit D
* 73deeed commit C
* c50221f commit B
* ef13725 commit A
所以一个比较好的 push 的流程应该是这样的:
# 当前分支 feature
git fetch origin master # 手动 fetch
git rebase origin/master
# 解决冲突问题
git rebase --continue
git push
或者直接使用 git pull --rebase
Ps:如果 --rebase 失败,可以使用
git rebase --abort
来回滚操作,如果意外使用了 pull 可以丢弃最新的 commit 来回滚
总结
都给我去用 IDEA