项目经验, 如需转载, 请注明作者: Yuloran (t.cn/EGU6c76)
前言
本文介绍如何使用 Git Bash 命令行, 提交代码, 解决冲突, 以及如何避免冲突. 有助于理解 Android Studio 的 VCS 背后的原理.
检出代码
检出远程仓库
Git clone https://github.com/Yuloran/GitTutorial.git
可以检出 origin/master 分支到本地, 这是 GitHub 创建仓库时默认的 主机名 / 分支名. 使用 Git branch -vv 查看本地分支状态:
可见, 本地分支名为 master, 关联的远程分支名为 origin/master(origin 是主机名, master 是分支名).
检出远程分支
很多时候, 配置管理员需要新建很多远程分支, 以进行同一项目不同版本的并行开发. 比如, 有的分支用于需求开发, 有的分支用于 Bug 修复等. 此时, 我们需要检出各自对应的分支, 修改并提交代码.
同步远程分支
管理员新建远程分支后, 我们需要先同步一下远程分支, 才能看到新建的分支:
如上图所示, 先使用 Git branch -a 查看本地和远程所有分支, 发现并没有管理员新建的 bug_fix 分支, 此时输入 Git fetch, 提示有一个新分支 bug_fix. 再次输入 Git branch -a 查看所有分支:
嗯, 确实多了一个 bug_fix 分支.
检出远程分支
Git checkout -b bug_fix -t remotes/origin/bug_fix
checkout -b 表示新建本地分支, bug_fix 为本地分支名, 你也可以起别的名字.-t 表示追踪远程分支 (track),remotes/origin/bug_fix 为远程分支名, 查看检出结果:
输入 Git branch 查看当前所在的本地分支:
输入 Git status 查看当前分支状态:
提示你目前修改是最新的, 没有任何修改可以提交.
提交代码
不良习惯
很多开发人员, 喜欢在一个本地分支上, 连续提交代码. 这是一个很不好的习惯, 尤其是在多人协作的情况下. 这会导致每笔提交之间存在依赖关系, 即使每笔修改之间毫无瓜葛. 进而可能导致 merge 冲突, cherry-pick 合入冗余代码. 而且, 如果你突然发现, 上上一笔提交有问题的时候, 我觉得你可能有种想 shi 的感觉.
正确姿势
保留一个本地分支, 专门用于同步代码.
比如, 我们现在需要在 master 分支上做一个需求, 首先输入 Git status 查看本地 master 分支的状态:
提示本地有修改文件, 没有提交. 咋整呢? 有两种处理方法:
啥也不管, 直接输入 Git pull 进行同步, 有冲突会自动合并, 合并不了再手动解决.-> 不推荐, 可能会在本地产生一条 merge 记录
先将本地修改 stash save, 再使用 Git pull --rebase 进行同步, 最后将暂存的修改 stash pop, 有冲突会自动合并, 合并不了再手动解决.-> 推荐, 自动变基, 不会在本地产生 merge 记录
1. 暂存代码
Git stash save [-u] 'update readme.md'
[-u] 表示参数可选, 加 -u 会将本地新增文件也暂存, 不加则仅暂存本地修改部分.'update readme.md' 为描述, 下面列出 Git stash 支持的所有操作:
Git stash list 显示所有暂存记录
Git stash show stash@{0}
查看指定的暂存记录
Git stash pop stash@{0}
弹出指定的暂存记录
Git stash drop stash@{0}
删除指定的暂存记录
Git stash clear 清空暂存记录
2. 同步代码
Git pull --rebase
同步结果:
提示已经是最新的. 如果本地代码不是最新的, 应当类似于下图:
3. 弹出暂存代码
Git stash pop [stash@{0}]
[stash@{0}] 表示可选, 不加默认弹出栈顶元素, 也可以指定弹出哪一个暂存记录. 弹出结果如下:
提示有冲突. 莫要惊慌, 有冲突解决就是了, 毕竟咱们干的都是 "小项目", 除非文件换行符变了, 否则不会冲突太多. 像 AOSP,Mokee 那种大型项目, 发生冲突才是坑爹. 比如国内的手机厂商, 每次大版本升级时 (比如从 Android 8.0 升到 Android 9.0), 都需要花几个月的时间才能使版本稳定, 这也是为什么国产手机安卓版本总是落后于 Google 的原因. 扯远了, 还是先 Git status 看一下工作区状态:
原来是 README.md 文件修改冲突了, 而且 Git 还贴心地提示你:
使用
Git reset HEAD <file>
来丢弃本地修改
使用 Git add <file>... 标记冲突解决 (省略号表示后面可接多个文件, 以空格分隔)
我们先使用 Git diff <file> 看看哪里冲突了:
Git 使用:
- <<<<<<<Updated upstream
- =======
- >>>>>>> Stashed changes
标记冲突状态,======= 上面的是远程仓库上别人的修改, 下面的是我们的本地修改. 嗯, 这个冲突是我人为制作的, 所以比较简单. 在 IDE 中手动解决该冲突后, 使用 Git add README.md 命令标记冲突已解决:
README.md 咋变原谅色了呢? 因为我们刚才用了 Git add 命令, 将其添加到了暂存区, 所以上面显示的是 Changes to be committed, 也就是待提交. 提交啥啊, 刚解决完冲突, 需求还没做呢! 所以, 我们使用 Git reset <file>... 命令, 将其从暂存区撤出:
<file>... 表示可选, 不加即撤出所有, 加了即撤出指定的文件. Linux 帮助手册中很多使用 <arg> 或者 [arg] 表示参数可选,<> 和 [] 是不需要输入的, 这个已经成为开发人员的习惯用法.
4. 新建本地分支
很多人这个时候, 就直接在本地 master 分支上疯狂输出需求代码了. NO! 我们应该针对不同的开发内容, 新建不同的本地分支. 比如 feature_shopping,bugfix_tombstone 等等, 假设我们现在需要实现一个购物功能, 我们应该使用 Git checkout -b feature_shopping 新建一个本地分支来实现这个需求:
5. 提交代码
连续通宵 5 天后, 我们的需求终于做完了, 可以提交代码了:
Git commit -m "update README.md" 表示将修改提交到本地仓库, 此时还没有推送到远程仓库.-m 后面的是修改描述, 这是一种简便写法. 而大公司都会对提交的描述有格式要求, 所以需要先配置 commit 模板:
Git config --global commit.template ~/.gitmsg
编辑该模板:
输入 Git commit:
模板已经生效了, 输入修改描述即可. 我这里配置的 Git 编辑器是 VIM, 你也可以配置成别的:
Git config --global core.editor notepad
这样, 就可以用记事本来编写修改描述了.
6. 追加提交
commit 之后, 本地又修改了一些文件, 此时需要使用 Git commit --amend 追加提交:
7. 回退提交
commit 之后, 发现提交多了, 把不需要提交的也提交了, 此时需要回退, 有两种方式:
Git reset [--soft] commit_id
, 软回退, 不会丢弃文件修改记录,--soft 不加也可以.
Git reset --hard commit_id
, 硬回退, 丢弃所有修改. 一般仅在需要回退到指定节点验证问题时使用.
查看 commit_id:
Git log -1
-1 表示只查看提交记录里的最后一条:
输入 Git reset 306c8b26360adfbdb3992f62514e8d58626f2d20, 即可回退提交. 然后重新 Git add <file>...,Git commit.
8. 推送代码
commit 之后很多人就直接 Git push 了, 这是不对的, 应当先同步代码. 由于我们现在在新建的本地分支 feature_shopping 上, 这个分支没有关联远程分支, 所以无法也不应该使用 Git pull --rebase 来同步代码. 正确的操作为:
Git checkout master
: 切到本地主分支
Git pull --rebase: 同步代码
Git checkout feature_shopping
: 切换到本地需求分支
Git rebase master: 将本地主分支代码, 合入到本地需求分支 (可能有冲突, 按照 Git 的提示修复即可)
Git push origin HEAD:refs/for/master
: 将本地需求分支的提交推送到远程 master 分支
结语
Git Bash 每一个命令的操作结果, 成功或者出错的描述都很详细. 遇到问题的时候, 只要按照提示, 一步步操作, 一般都能解决.
来源: https://juejin.im/post/5c2f4e07f265da61483bbf4b