作为一种功能强大的项目管理工具,git不仅能够实现方便的版本控制,还能够进行多人开发的项目管理。本篇博客主要汇总一些常用的git操作,作为笔者自己的复习巩固,亦作为读者之间的交流探讨。
1. 高频操作
git init : 初始化仓库,工作区多了一个.git 目录用于存放仓库信息。git add <filename> : 将工作区的改动存放到暂存区。可以指定文件名,也可以加-A 参数,表示针对仓库所有文件。git commit -m <comments> : 将当前暂存区存储的所有改动,存入一个commit。commit类似于一个快照/存档,你当然可以随意读档、存档。如果git commit出错了怎么办,参见笔者另一篇博客【git】git commit -m的message输错咋办。git status : 查看当前仓库所有文件的状态。例如,哪些文件被改动了。并提供一些可能命令。git diff <filename> :查看文件相对于最近一次暂存(上一次add)的改动。
2. 版本控制
所谓控制‘版本’,其实就是管理commit。
git reset --hard <commit_id> : 切换版本,不仅可以切换过去版本,还能从过去版本切换回来,只要你知道commit_id。commit_id不仅可以用绝对位置,还可以用HEAD指针表示的相对位置指代。例如,HEAD^ 表示上一个版本,HEAD^ 表示上上个版本,HEAD~100 表示前100个版本。git log : 查看提交历史, 由近到远显示,仅包含commit命令,可以用来获取过去的commit_id。笔者喜欢加上一些参数使用git log --graph --pretty=oneline --abbrev-commit ,效果如下:
git reflog : 查看命令历史,包括commit在内的所有历史命令及其id,可以用来获取未来的commit_id。
3. 修改管理
所谓管理‘修改’,指的是对工作区、暂存区修改的管理,而非针对于commit。管理commit参见上一节,版本控制。
git restore --staged <file> : 将指定文件的改动,从暂存区撤回。相当于git add的逆操作。git restore <file> :将指定的文件的改动,从工作区撤回。相当于直接退回上个commit版本。git stash :将当前工作区的状态保存。一般适用于,当前分支工作区的修改还未commit,需要切换分支进行其他操作(例如,修改其他分支的紧急bug)。此时,若不通过stash保存工作区,当前分支工作区的修改将丢失。git stash list :查看所有保存的工作区。git stash apply stash@\{0\} :将某个保存的工作区恢复到当前版本。此处,以序号0为例。注意,shell里面要用\ 转义{} 。git stash drop stash@\{0\} :删除某个保存的工作区。git stash pop :git stash apply + git stash drop 。使用前先确认,stash list顶端的那个工作区,是你要恢复的工作区。
4. 远程关联和同步
远程关联
如果想要将本地仓库push到远端,需要提前和远端仓库(例如,github的仓库)建立关联:
git remote add origin <url> : 添加一个名为origin的远程库。origin 是通用的仓库名称,也可以自定义。git remote rm origin : 和origin解除关联,如果add的时候url错了。
如果想要从远端拉取一个仓库:
git clone <url> : 将某个仓库拉取到本地,自动建立关联。推荐用ssh协议; 如何在远端添加本地机器的ssh密钥,详见笔者另一篇博客添加github ssh授权。
远程同步
git remote -v : 查看远程库地址信息。git pull origin <src> : 将远端origin仓库的src(一般为某个分支,如master),拉取到本地仓库。git push origin <src> : 将本地仓库的src(一般为某个分支,如master),推送到origin仓库。git pull --rebase :rebase操作可以把本地未push的分叉提交历史整理成直线,但个人不建议使用。
有些时候,我们不想让某些文件被push到远程进行同步,比方说,vscode的配置文件,编译中间文件等。这个时候我们可以写一个.gitignore 文件,将我们不想要加入git仓库的文件写入。这样,git在进行项目管理的时候,就会.gitignore 中指定的那些文件。
笔者通常使用如下的.gitignore ,用来忽略所有. 开头的隐藏文件,.gitignore 和.gitkeep 除外。
.*
!.gitignore
!.gitkeep
5. 分支管理
git branch :查看分支。当前分支前面会有*号。git branch <name> :创建分支。git switch <name> :切换分支。git switch -c <name> :创建+切换分支。git merge <name> : 合并某分支到当前分支。笔者喜欢在合并时,加上--no-ff 参数,用以防止fast forward。git branch -d <name> :删除本地分支。如果被删除的分支还有未被merge的内容,需要强制指定-D 删除。git push origin --delete <name> : 删除远程分支。git cherry-pick <commit_id> :将某个特定的commit的改动,复制到当前分支。可以将其他分支的commit应用过来。cherry-pick的目的主要是为了防止重复劳动。使用场景,例如,发现master分支有bug,由于dev分支是fork自master,因此dev分支也需要相同的操作修复相同的bug。所以,在master分支修复bug之后,可以用cherry-pick,将master分支修复bug的那个commit直接apply到dev分支。也可以用在修改commit message,详见【git】git commit -m的message输错咋办。
6. 标签管理
一个标签和一个commit密切相关,标签存在的意义在于,commit_id不易于理解交流。因此,对于某些比较重要的commit,可以给其附上tag,方便定位、回溯和发布。
git tag :查看所有标签。git tag <tagname> <commit_id> : 给指定的commit,打一个指定名字的标签,例如,v1.0。若不指定 commit_id,默认打在当前分支,最新的一个commit上。git show <tagname> :查看指定标签信息,例如,commit_id。git tag -a <tagname> -m <message> <commit_id> :使用 -m <message> 还可以为这个tag附上额外的message。git tag -d <tagname> : 删除一个标签。
由于git push origin branch的时候,并不会将该branch的tag也push上去,所以本地的tag需要独立地push。
git push origin <tagname> : 推送某个标签到远程。git push origin --tags : 一次性推送全部尚未推送到远程的本地标签。git push origin :refs/tags/<tagname> : 如果需要删除某个远端已经上传的标签,则需要先使用git tag -d 先把本地的标签删除,在使用该条命令,将标签删除操作,同步到远程。
7. 其他补充
-
git的精髓在于,它管理的是你文件的改动,而非文件本身。 -
commit在git版本管理中起到很重要的作用,合适的comment可以帮助你迅速定位和回溯。所以,不同commit之间尽量逻辑分明。在进行开发时,能add的改动就先add暂存起来,等到一个功能具体的、逻辑完整的改动完成之后,再commit。 -
git有工作区和暂存区之分。所谓工作区,就是指你本地的这个文件夹内的所有文件。工作区内的改动在你关掉IDE之后就没了(因为一旦断电,你就没办法ctrl+z,回溯到你之前的文件版本,你就不知道你改动的部分到底是啥);而git add就是将你工作区的改动暂存起来,便于你随时离开;而git commit则是将你所有暂存的改动,一次性存成一个正式的存盘,可以在以后随时回溯。 -
git强烈建议多用分支。增加功能 / 修复bug,都建议新建一个分支,等完成任务之后merge到主分支,再删除新分支。 -
git的每个branch都有各自的指针,用于指向当前的commit id,指针值改变,commit版本就改变;git还有一个全局的环境变量–HEAD ,用来指向当前分支的指针,HEAD的值改变,当前所在的分支也就改变。因此,git 版本 / 分支切换速度很快,因为git的版本 / 分支切换只是改变HEAD / 分支指针的值罢了。 -
在远端同步和分支合并的时候,很容易就出现conflicts。一般是由于相同的文件,被不同地修改,此时需要手动修理冲突,并进行额外的commit。Git用<<<<<<< ,======= ,>>>>>>> 标记出冲突内容。 -
引用廖雪峰的官方网站,针对多人协作,远程同步时,经常会出现conflicts,一般用以下原则应对: 1.首先,可以试图用git push origin <branch-name> 推送自己的修改; 2.如果推送失败,则因为远程分支比你的本地更新,需要先用git pull 试图合并; 3.如果合并有冲突,则解决冲突,并在本地提交; 4.没有冲突或者解决掉冲突后,再用git push origin <branch-name> 推送就能成功! 5.如果git pull 提示no tracking information ,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to <branch-name> origin/<branch-name> 。 -
git不仅可以帮助多人协作,就算是你自己平时本地开发,也可以用git方便的切换/回溯不同的版本(commit) -
根据笔者的经验,一般而言,我们在新开始写一个项目的时候,都会自然地把工作区初始化为一个git仓库,利用git便捷的版本控制系统来协助自己本地的开发。如果开发者只有自己,那可以等正式版本确定之后,再push到远程,不用每次commit一个存档,都push一次;当项目不止一个人参与时,就需要随时同步到远程仓库,和其他同伴进行协作,及时解决conflicts,这种情况下,最好commit一次,就push一次。 -
git本地的分支可以不推送到远端,完全看个人需要。 -
引用廖雪峰的官方网站,在实际协同开发中,哪些分支我们需要时刻和远端保持更新呢? 1.master分支是主分支,因此要时刻与远程同步; 2.dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步; 3.bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug; 4.feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。 -
引用廖雪峰的官方网站,在实际协同开发中,我们应该按照几个基本原则进行分支管理: 1.首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活; 2.那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本; 3.你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。 4.所以,团队合作的分支看起来就像这样:
参考
本文主要参照廖雪峰的git教程,该教程还囊括了git服务器搭建,git命令别名,以及sourcetree的使用等,这里笔者强烈安利初学者进行学习。
|