前言
在大型项目的协同开发过程中,我们需要从远程代码仓库拉取代码到本地,在本地代码仓库进行某个分支上的开发,再将本地的代码同步到远程仓库。对于初学者而言,往往会碰到各种各样的问题,即便是有一定经验的开发者,有时对项目中使用到的Git命令也是一知半解。本文从真实项目的开发过程中抽象出了一些场景,并针对各个场景给出了相应的解决方案,在实战中理解Git的常用命令。
Git的使用方式
Git主要有两种使用方式,命令行和可视化界面。
命令行
安装完Git后,如果安装时(默认)选择了如图这一项,那么除了在git bash之外,也可以在cmd里使用git。如果是Linux环境,直接到工作区路径下使用即可。
- 自带的Git bash
- cmd
可视化界面
- 自带的Git Gui
- Github Desktop
- VScode源代码管理(附对照表)
项目实战
实战中用到的命令感兴趣可以借助文末参考资料或者ai拓展学习,我将步骤拆解得尽量小方便你理解其中原理,熟练后很多操作可以合并,或者有更简单的实现方式。
第一天
经历无数面试,你终于来到单位上班,你的导师向你介绍即将加入的项目,然后发给你项目地址,于是你进行了以下操作:
- 在本地建了一个文件夹,克隆(git clone)仓库,创建了一个文件夹作为你的工作区;
- 经验丰富的你知道本地也要避免直接在 main 或 master 分支上进行开发,于是创建并切换到一个新的分支"new-feature";
git checkout -b new-feature
- 熟练地查看当前暂存区和分支状态;
git status
- 并确认了本地所有分支,看到除了默认的main分支之外,多了新建的分支"new-feature";
git branch
第二天
导师看你骨骼精奇于是在远程仓库给你单独创建了一个"remote-new-feature"远程分支,给了你一个小需求让你把代码提交到这个分支上,于是你进行了以下操作:
- 完成本地代码编写,简单写了个test文件,在本地编译后初步验证了自己代码的正确性,此时本地仓库变成类似这样——
- 然后你要把需求代码,也就是workspace目录下的内容以及构建文件CmakeLists.txt的修改提交到远程仓库,供其它成员调用。首先将本地修改添加到本地暂存区;
git add workspace CmakeLists.txt
- 将暂存区内容提交到本地仓库中,并在弹出的vim编辑窗口中添加提交时间、用户名等提交信息;
git commit
- 检查当前分支的提交记录,并简洁地显示,你确定了游标HEAD指向的是你在"new-feature"分支上刚刚的提交;
git log --oneline
- 更新本地记录的远程分支信息,确认信息中包含了准备推送的目标分支;
git fetch
git branch -r
- 将当前分支"new-feature"与远程分支"remote-new-feature"绑定,术语叫“为现有本地分支设置远程跟踪分支”。然后顺手检查了一下跟踪关系;
git branch -u origin/remote-new-feature
git branch -vv
- 绑定之后既保证了正确性,又方便了未来拉取和推送代码的操作,至此,你心满意足地推送本地仓库的提交到远程仓库;
git push
- 由于本地分支和跟踪的远程分支名不同,于是得到了这样的提示——
- 于是你按照提示推送并指定本地分支和远程分支,推送成功;
//origin默认指克隆的源仓库,本地分支名可以用HEAD表示当前分支
git push <远程仓库名> <本地分支名>:<远程分支名>
- 最后你去网页上提了个合并请求pull request,导师检查无误后将你的分支代码合并到了主分支上,并提醒你下班记得打卡。
第三天
当两个或更多的分支尝试合并时,如果在同一行或同一文件的同一区域有冲突的更改,Git 将无法自动合并这些更改,从而产生冲突。
今天,导师给了你一个新的需求,并介绍了一名同事与你合作完成这个需求,同事比你先推送了代码,于是你进行了以下操作:
- 拉取远程分支的最新代码,发现有冲突拉不下来;
git pull
- 于是你按照提示先储藏未提交的更改,临时保存工作进度,在拉取最新代码后再弹出储藏的更改;
git stash
git stash pop
- 此时产生了代码更改的冲突,于是你打开发生冲突的文件,看到经git标注过的两份代码,一份是同事写的,一份是你写的,你果断将同事的代码删除,保存退出,使用git add将冲突文件加入暂存区,标记为解决;
- 你觉得用命令行查看冲突比较麻烦,于是借助可视化界面快速定位每一个冲突点解决冲突;
- 冲突解决之后,继续第二天的操作,成功将代码推送到远程分支,你满意地点了点头,打开了黑悟空......
第四天
git reflog
是一个非常有用的命令,它可以显示你的 HEAD 以及重要引用的变动历史。即使你使用git reset --hard
改变了历史,reflog
也会保留这些信息。
这天,你刚到公司打了个卡,正在美美地享用早餐,同事突然气呼呼地冲过来质问你,他昨天提交的代码呢?你尴尬地摸了摸鼻子,向他保证今天一定找回来,于是早会结束后你进行了以下操作:
- 由于错误已经推送到远程仓库,你担心被导师发现,打算使用git reset和git push --force清理现场,这时同事突然通知你他又推送了一次,让你回退顺便把新代码合入了。于是你只能再git pull一下,创建一个新的提交来撤销指定提交,顺便解决了新代码的冲突;
git revert 4c16e7f
- 提交到本地仓库之后你突然发现有个地方改错了,于是撤销提交和暂存区,修改后再次提交;
git reset
- 你告诉同事已经搞定了,同事却说突然接到了一个紧急通知,新版本出了问题,要求你们紧急回退到上个版本,大概是三个提交之前,于是你没多想就回退三次提交及工作区;
git reset --hard HEAD~3
- 回退完一看,糟了,多回退了一次,你想用远程仓库更新本地代码重新回退,突然想起最新提交的代码没有推送到远程仓库,所以现在好像本地远程都没有那次提交的信息了,怎么办?机智的你想到了查看历史操作记录,切换分支或恢复文件到指定提交,这种方式切换到特定的提交时,Git会进入处于分离头指针(detached HEAD)状态;
git reflog
git checkout <你想找回的提交编号>
- 由于分离头指针状态下的更改不属于任何分支,会在切换分支时被丢弃,于是你按照提示创建了一个新的分支tmp来关联这次提交的内容,并切换到工作分支。最后,将tmp合并到工作分支,终于回到了目标版本,开始了新一轮的工作。
git merge tmp
结语
掌握了以上命令的使用,可以应对绝大部分场景的增删改查,而分支模型之所以是Git最强大的特性,它的上限远不止这些,比如Git Flow分支模型,比如交互式变基,Cherry-Pick等,用于更高效地处理更复杂的场景。
像Git一样,自由且强大,祝你也祝我
The End
参考资料: