Git使用小结
本文总结在工作中用到的Git
相关内容,可分为基础概念、配置、本地开发、远程仓库等四个部分。
基础概念
Git
将待管理数据当做一个整体,每执行一次 git commit
,都是对代码仓库整体做一次完整备份。
Git
对每份文件都会计算校验和,用来校验和引用。有一个HEAD指针,始终指向当前所在分支的最近一次提交记录。
每一次 clone
会将远程仓库中的每个文件的每个版本都拉取下来。
Git
有工作区、暂存区、本地仓库、远程仓库四个概念,被管理的文件是从前一个区逐个移到后一个区,Git
提供各种命令来支持这些移动操作。注意,提交操作的对象始终是暂存区。
配置
Git
的配置,从上到下分为系统级、用户级和仓库级,可通过如下命令查看:
git config --system <-- 系统级别的配置 对应 /etc/gitconfig
git config --global <-- 当前用户的配置 对应 ~/.gitconfig
git config --local <-- 当前仓库的配置 对应 .git/config
git config --list --global <-- 查看当前git配置
除了上述基本配置,还可以配置一些 Git alias
命令,方便日常使用。在这里,不建议配置太多,下图为笔者目前的配置
最常用的是 git st
和 git last
。
上图中的 pull.rebase = true
,表示使用rebase
来替换merge
操作。rebase.autoStash = true
,表示在rebase
时,自动将当前修改stash
,合并完成后再恢复当前修改。这两个设置建议配合起来使用。
命令行的Git在查看大量内容时体验不好,对于查看改动/日志、删除文件、文件改名等操作,使用 TortioseGit
图形化工具更合适。
Git
仓库的忽略文件列表 .gitignore
是逐步完善的,将动态生成的、无需关心的文件排除,可提高变动查看效率。
可使用 git check-ignore -v filename
查找文件是否在忽略列表中,如果有内容,则表示已纳入忽略列表。
常见问题
本地在commit
后push
,发现 push
时,会提示远程仓库有更新,要先 pull
,执行完 pull
后,日志记录中会多除 Merge branch
的提交节点。
这是因为git pull
的默认操作是 git fetch
(拉取数据到本地) 和 git merge
(合并数据) 的组合。在merge
时,会生成合并节点提交。
解决方案: 将 pull
操作中的合并操作,改为 rebase
。使用 git config --global pull.rebase true
该命令。
本地开发
本地开发就很简单,修改后add
,将一次目标相关的文件都add
后,可以commit
了,为了减少冲突,在commit
之前,一般先执行pull
,同步远程更新后,再push
。
查看本地原始版本 与 本地修改后版本的区别, 使用 git diff
。
查看本地原始版本 与 本地修改后版本并add的区别, 使用 git diff --staged
。
将add
和commit
合并在一条指令中,可执行 git commit -am "XXXX"
。注意,这个操作不会对未add
的文件生效。
日志查看
建议用 TortioseGit
来查看日志,直观且效率高。
如果某次改动较少,可用 git last
找最后几次提交 ,配合 git show commitId[:filename]
来查看某次特定提交的文件更改。
git diff
只查看文件改动的差异,git show
在查看文件改动的基础上,增加显示提交注释信息。
日志查看命令:
git diff branch1 branch2 --stat 显示差异的统计部分
git diff branch1..branch2 查看 branch2 中比 branch1 中多提交了哪些内容
git log featureA..origin/featureA 查看从 featureA 开始到 origin/featureA 的提交,不包括featureA的提交
也可以理解为在 origin/featureA 分支中,而不在 featureA 分支中的提交。
git log -S ZLIB_BUF_MAX --oneline 在提交中查找 ZLIB_BUF_MAX 的新增、修改、删除等提交操作
git log --grep XXXX 支持在日志中搜索关键字
历史修改查看,建议直接使用 TortioseGit
的Blame
功能,对应 Git
命令是git blame -L 69,82 fileName 查看某个文件某行的历史修改记录
处理提交记录
-
修改提交注释
参见 修改commit注释 ,核心为
git commit --amend
指令。
若要将改动后的提交注释更新到远程仓库,push
时要加上--force
-
修改提交记录
假设当前的提交记录是 A --> B --> C --> D , 此时想要删除 B 的提交,可以这样
方法一:
git reset A --hard 先回到A状态
git cherry-pick C D 再将C和D应用到A上如果待处理的提交记录较多,可用 git rebase -i A, 此命令以A状态为基准,将A之后的commit,按时间熟悉,从上到下,依次列出来。只需要将 **不需要的提交** 前的 pick 改为 drop,保存退出,解决冲突后,执行 git rebase --continue 就能达到目的。
方法二:
git revert -n B
处理完冲突后,执行 git add fileName 标记解决冲突
然后执行 git revert --continue 继续回滚流程
git commit -m "回滚B次修改" -
重返未来:使用
git reflog
查看命令历史,以便确定要回到未来的哪个版本。
分支管理
分支合并有三种方法,merge
、rebase
和cherry-pick
。
- 当需要整合其他分支的全部改动到当前分支,并且将整合操作当做一次提交,用
merge
- 当需要整合其他分支的全部改动到当前分支,整合操作不充当一次提交,用
rebase
- 当需要整合其他分支的部分改动到当前分支,整合操作不充当一次提交,用
cherry-pick
以整合 experiment
分支改动到 master
为例,介绍各自操作流程:
- merge
git checkout master <-- 切换到 master 分支
git merge experiment <-- 将 experiment 分支的改动全部应用到master分支,并将整合操作当做一次提交(如果在merge前,master分支没有提交,则不会生成整合节点)
- rebase
git checkout experiment <-- 切换到 experiment 分支
git rebase master <- 将当前分支的改动,应用到 master 分支
如有冲突,解决冲突后执行 git add 将冲突标记为解决
解决完所有冲突后,执行 git rebase --continue
git checkout master <-- 回到 master 分支
git merge experiment <-- 进行一次快速合并
使用变基的好处在于,尽管开发工作是在多分支上并行开发,但从提交记录上看是串行的,提交记录没有分叉。
- cherry-pick
git checkout experiment
git log <-- 记住需要迁移的commit-id
git checkout master
git cherry-pick commit-id <-- 将需要迁移的commit-id合并到master
合并过程中会遇到冲突
方案1:用户解决冲突后,将修改后的文件加入暂存区,然后使用 git cherry-pick --continue,继续合并流程
方案2:放弃合并,回到操作前的样子 git cherry-pick --abort
方案3:退出,回不到操作前的样子 git cherry-pick --quit
新建并切换分支: git checkout -b featureA
删除本地分支: git branch -d 本地分支名
删除远程分支 git push origin --delete 远程分支名
修改暂存
- git stash 暂存已add,但还没有commit的修改
- git stash list 查看已暂存的修改
- git stash clear 清除已暂存的修改
- git stash apply --index 将最近暂存的修改重新应用,恢复到原状。
- git stash apply stash_id 可恢复旧的暂存数据
- git stash drop stash_id 删除指定stash_id的数据
- git stash show -p stash_id 查看某次stash的内容
使用注意:
- 暂存数据可在不同分支之间使用,需要手动清除。
- 暂存操作最好只保留一份,用完及时清理,同时存在多个暂存,很容易搞混,出错。
远程仓库
查看远程仓库的详细信息: git remove -v
添加远程仓库: git remote add <shortname> <url>
, 添加指定远程仓库,并绑定一个简称,后续可用该简称来代替远程仓库地址。
在本地工作了一段时间后,如果远程仓库有更新,本地如何同步?
假设本地为 master
分支,远程为 origin/master
分支。
- 执行 git fetch
获取指定远程仓库的所有更新,此时本地不会有远程分支的可编辑副本。 - 执行 rebase 来将远程分支内容合并到当前分支
也可以 git checkout --track origin/serverfix 自动新建本地同名分支并且跟踪远程serverfix分支
git push 远程仓库简称 本地分支名:远程分支名, 如果本地分支和远程分支同名,则可以省略冒号。
将本地分支关联到已存在的远程分支上:
git branch --set-upstream-to=origin/远程分支名 本地分支名
将本地分支关联到不存在远程分支上:
git push -u origin branch
查看不同分支的上游: git branch -vv 在每个分支名称后面即可看到对应上游的简称。
查看本地和远端的所有分支: git branch -a
在本地新建分支,并且关联远程特定分支,这两个分支名称建议保持一致: git checkout --track origin/V1.0
开源项目协作
// 1. 获取开源项目,并在本地新建分支修改
git clone <third_repo_url> 获取开源项目仓库
git checkout -b featureA 在本地创建 featureA 分支
work...
git commit
// 2. 在 Github 上 fork third_repo_url,得到自己的远程仓库地址,my_third_repo_url
git remote add myfork <my_third_repo_url>
git push -u myfork featureA 将 featureA 分支推送到自己的远程仓库上,形成新的远程分支。
这样做的好处是,如果工作不被接受,master分支不会受到影响。
// 3. 在 Github 上,向原项目作者发起 Pull Request 请求。
// 开源项目提交的作者是实际做出修改的人,提交者时将此项工作提交到仓库的人。
工程实践经验
在一个大工程中,可能同时有很多修改,有的是改Bug,有的是调试日志,有的是新需求。在Git
标准工作流中,会将这些改动按照分类推入到不同分支,等各分支完成后,再合并会主分支,这看起来很好,但是,它没考虑一个问题,就是切换分支对项目构建的影响。甚至从A分支切换到B分支,再切回A分支,会触发工程的编译,重编译范围与改动内容息息相关。对于大项目来说,工程重编译时间,少则十来分钟,多则半个小时到一个小时,这种切换带来的重编译耗时太大,即使用上 stash,也会触发相关文件的重编译,在笔者当前的工作场景下,不是很合适。
因此,在实际工作中,需求开发和改Bug都在个分支进行,按照每次提交最小功能原则,逐个将改动先add
,等到攒够最小提交模块后,再commit
。其他那些改动,比如打开调试、对配置文件的测试更改,不管是需求开发还是bug修复,都是需要的,保留在一起即可。
在本地建一个GitDemo
目录用于练习、验证Git
命令。
**Git GUI **的 bash
,支持多行输入,只需要在每一行后加 ** 就行。
注释规范
第一行以精简文字描述描述变更,接着是一个空白行,再接着是一个详细解释,即改动的动机和它的实现与之前行为的对比。
下面是在网上摘抄的提交规范:
日志提交注释标准规范:
<type>(<scope>): <subject> <-- 必须有 type 和 subject,scope可选
// 空一行
<body> <-- 可选
// 空一行
<footer> <-- 可选
type: 说明提交类型,有7个标识:
feat:增加新功能
fix:修补Bug
docs:完善文档
style:格式修改,不影响代码运行
refactor:重构
test:增加测试
chore:构建过程或辅助工具变动
scope:说明提交影响范围
subject:提交目的简短描述,动词开头,结尾不要句号。
body:对本次提交的详细描述,可分多行。
如果当前的commit针对某个issue,可执行 Closes #234 来关闭该issue
总结
本文梳理Git
在配置、本地开发、远程仓库等场景上的常用流程,并给出自己的一些实践经验,以备后续查询。