作为一名四有程序员咱最终还是要走命令行的路子 🔥🔥「Git命令总结,建议收藏」

3,964 阅读17分钟

目前团队内部看到不少小伙伴都在用Git的图形化工具,例如GitHub for Desktop、Source Tree、TortoiseGit(小乌龟)等等。我个人其实并不排斥用这些图形化工具,我也是从这些图形化工具上走过来的,但这就是一个过渡,作为一名有理想的程序猿咱最后还是要走命令行的路子。况且没觉得敲命令行的小哥哥或者小姐姐更nice么?不说笑了,其实在咱们的开发过程中很多时候真的命令行比工具更可靠,有的疑难杂症真的需要命令行才能解决。图形化工具就是一个封装了的组件,可塑性不如原生的命令行来的实在。下面就聊一聊Git吧,其实Git没那么难。

Git简介

Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

i-1.png

Git官网的介绍是这么说的:Git是一个免费的开源分散式版本控制,旨在以高效的速度处理从小到大的项目。(低调!太低调了!)

Git是目前世界上最先进的分布式版本控制系统(没有之一)。

Git可以说是一名程序员的必备技能,对于工作和面试都是非常有帮助的,因为无论是github,还是gitlab,还是其他的代码托管平台,代码管理都是用git去做的。

Git诞生

在介绍 git 的相关操作前,我觉得非常有必要了解 git 的由来。Linus Benedict Torvalds(林纳斯·本纳第克特·托瓦兹),这个人我相信大家都知道吧,开源 linux 系统的发明人。

i-2.png

如今,你看到的大部分服务器其实都是运行在linux系统上,令人感到称叹的是,这位大神级别的程序员不仅创造了 linux 系统。2005他还花了两周时间自己用 C 写了一个分布式版本控制系统,也就是我们这篇文章要说的东西 Git。

这时有的小伙伴就要问了:当时就没有代码版本控制系统么?为什么他要自己手撸一个呢?

答案是:有的。

又有小伙伴又要问了:那为他什么他不用呢?是大神的喜欢自己动手丰衣足食么?

答案是:并不是这样的

这里就要聊一点八卦了,这之间是有故事的。2002年之前, Linux 代码的管理方式是世界各地的志愿者把源代码文件通过 diff 的方式发给 Linus,然后由 Linus本人通过手工方式合并代码!要知道,当时的 linux 的代码量已经很大了,通过人工管理的方式,一是容易出错,二是效率低。于是 Linus 选择了一个商业的版本控制系统 BitKeeper,BitKeeper 的东家 BitMover 公司出于人道主义精神,授权 Linux 社区免费使用这个版本控制系统。

大好局面在2005年就被打破了,因为有人的地方就有江湖啊,何况Linux社区牛人聚集,不免一些沾染了梁山好汉江湖习气的人。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了(小样 监控工作做得不错!),于是BitMover公司怒了,要收回Linux社区的免费使用权。

原本Linus可以向BitMover公司道个歉,保证以后严格管教弟兄们,其实事情也就OK了,但大神么都是骄傲的,怎么可能道歉嘛,自己撸一个出来给兄弟们玩玩不就可以了。于是 Linus 花了两周时间自己用 C 写了一个分布式版本控制系统,这就是 git 的由来了。(牛就是这么定义的!体会一下。)

之后Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。

历史就是这么偶然,如果不是当年BitMover公司威胁Linux社区,可能现在我们就没有免费而超级好用的Git了。

GIT原理

要想弄懂 git 是怎么进行代码管理的,那首当其冲的是了解 git 的工作区域是如何构成的。因为,只有弄懂了 git 工作区域的构成,你才可以在适当的区域使用合适的命令。如下图所示

i-3.jpeg

workspace

工作区,就是平时进行开发的地方,开发的过程也就是对工作区的操作

Index

暂存区,当完成某个需求或功能后需要提交代码,第一步就是通过 git add 先提交到暂存区,执行git add 的命令后,工作区的文件就会被移到暂存区,暂存区标记了当前工作区中哪些内容被 git 管理。

Repository

本地仓库,位于自己的电脑上,通过git commit 提交暂存区的内容,会进入本地仓库。

Remote

远程仓库,用来托管代码的服务器,远程仓库的内容能被分布在多个地点且处于协作关系的本地仓库修改,本地仓库修改完代码后通过 git push命令同步代码到远程仓库。

一般来说,Git 的工作流程分为以下几步:

  1. 在工作区开发,添加或修改文件。
  2. 将修改后的文件放入暂存区。
  3. 将暂存区域的文件提交到本地仓库。
  4. 将本地仓库的修改推送到远程仓库。

GIT安装

在 Mac 上安装 Git 有多种方式。 最简单的方法是安装 Xcode Command Line Tools。 Mavericks (10.9) 或更高版本的系统中,在 Terminal 里尝试首次运行 git 命令即可。

git --version

如果没有安装过命令行开发者工具,将会提示你安装。 如果你想安装更新的版本,可以使用二进制安装程序。 官方维护的 macOS Git 安装程序可以在 Git 官方网站下载,网址为 git-scm.com/download/ma…

i-3.png

GIT使用

项目克隆

i-4.png 如图项目克隆分为SSH和HTTPS两种:

SSH 克隆

git@newsgitlab.xxx.com:xxx-ai/frontend/ai-app-vue-boss.git
1、生成ssh - key

打开终端在终端输入以下代码:

ls -al ~/.ssh

如果输出内容里边包含 id_rsa(私钥)、id_rsa.pub(公钥),则直接跳过去下一步(获取公钥和私钥):

i-6.png 如果输出如下则表示你的电脑没有生成过公钥和私钥,则要进行生成操作:

No such file or directory

在终端执行

ssh-keygen -t rsa -C"your_email@xxx.com"

代码参数含义:

-t 指定密钥类型,默认是 rsa ,可以省略。

-C 设置注释文字,比如邮箱。

-f 指定密钥文件存储文件名。

以上代码省略了 -f 参数,因此,运行上面那条命令后会让你输入一个文件名,用于保存刚才生成的 SSH key 代码,如:

i-7.png 之后,会询问你是否需要输入密码。输入密码之后,以后每次都要输入密码。请根据你的安全需要决定是否需要密码,如果不需要,直接回车:

i-8.png 至此ssh-key已经创建完成,可以进行下一步操作了

2、获取公钥和私钥

在终端执行以下操作

进入.ssh目录

cd ~/.ssh

查看文件目录

ls

在终端查看私钥(私钥名称默认为id_rsa)

cat id_rsa

在终端查看公钥(公钥名称默认为id_rsa.pub)

cat id_rsa.pub

i-9.png ps: 如果生成key的时候填写了文件名称,则记得替换为你自己输入的文件名

3、配置秘钥

这里以 gitLab为例,如下图所示,点击头像 => 设置 => SSH 密钥, 通过cat命令查看文件id_rsa.pub的内容,然后复制过来,点击添加秘钥

i-10.png

HTTPS 克隆

git clone https://newsgitlab.xxx.com/xxx/xxxxx/ai-app-vue-boss.git

由以上两个链接不难看出,SSH和HTTPS 克隆项目的差别主要是所用协议不一样

HTTPS用443端口,可以对repo根据权限进行读写,只要有账号密码就可进行操作。

SSH则用的是22端口,也可以对repo根据权限进行读写,但是需要SSH Key授权,这个key是通过SSH key生成器生成的,然后放在github上,作为授权的证据,这样的话就不需要用户名和密码进行授权了。如果配置SSH Key的时候设置了密码,则push的时候需要输入密码的.

分支管理

#查看本地分支 
git branch

#查看远程分支 
git branch -r

#查看本地和远程所有分支 
git branch -a

#创建一个空白分支且不切换 
git branch

#重命名分支 
git branch -m <oldbranch-name> <newbranch-name>

#删除本地分支 
git branch -d <branch-name> #会在删除前检查与上游分支或者与head的merge状态

#删除本地分支 
git branch -D <branch-name> # git branch --delete --force的简写 (强制删除)

#拉取分支 
git checkout -b <branch-name> #基于当前分支拉取新分支并切换到新分支

#拉取分支 
git checkout -b <branch-name> <指定的分支名或commit值> #基于指定分支拉取新分支并切换到新分支

#切换分支 
git checkout <branch-name> #切换到指定分支

#合并分支 
git merge <branch-name> #指定分支合并到当前分支

#查看已合的分支 
git branch --merged #查看哪些分支已经合并到当前分支

#查看未合的分支 
git branch --no-merged #查看哪些分支没有合并到当前分支

#查看分支最后一个提交的信息 
git branch -v #查看各个分支最后一个提交对象的信息

# 删除远程分支 
git push origin --delete <branch-name> #删除远程分支 (--delete 可简写为-d)

#获取远程仓库特定分支的更新 
git fetch <远程主机名> <branch-name> #获取远程仓库特定分支的更新

#获取远程仓库所有分支的更新 
git fetch --all/origin #获取远程仓库所有分支的更新

#拉取远程分支并创建本地分支 
git fetch origin <branch-name>:<local-branch-name> #拉取远程分支并创建本地分支

代码提交

#查看修改 
git diff #查看修改的内容(提交需谨慎、查看很重要!)

#添加到暂存区
git add <file-name> # 添加某个文件到暂存区,后面可以跟多个文件,以空格区分

#添加到暂存区
git add . #添加当前更改的所有文件到暂存区

#提交暂存的更改
git commit  # 提交暂存的更改,会新开编辑器进行编辑提交的注释

#提交暂存的更改
git commit -m "you message"  #提交暂存的更改,并写下提交更改的备注
 
#添加到暂存区并提交暂存的更改
git commit -am "you message" #添加修改的内容内容到暂存区并提交,等同于git add . 和 git commit -m
 
#对最近一次的提交的信息进行修改,此操作会修改commit的hash值
git commit --amend "you message"

代码拉取和推送

#拉取代码 (等同于 git fetch && git merge)
git pull  #拉取当前分支远程的最新代码 
git pull <远程主机名> <远程分支名> #把远程分支更新到当前的本地分支
git pull --rebase <远程主机名> <远程分支名> #使用rebase的模式进行合并
git pull <远程主机名> <远程分支名>:<本地分支名> #把远程分支更新到本地指定分支
 
#推送代码
git push #把本次提交推送到远程
git push --force  #强制提交 --force可简写-f
git push <远程主机名> <远程分支名> #将本地仓库某分支推送到远程仓库上
git push <远程主机名> <本地分支名>:<远程分支名> #将本地分支推送到远程仓库的不同名分支
git push <远程主机名>  :<远程分支名>  #这里省略了本地分支,也就相当于将空白内容推送给远程分支,就等于删掉了远程分支

代码暂存

当我们想要暂存文件,切换分支做某些事的时候,可以用 git stash 这种机制帮助开发

#暂存代码 
git stash #暂存代码 
git stash save "message..." #推荐暂存的方式 
git stash list #查看暂存列表 
git stash pop #取出暂存 同时删除暂存里面对应的取出的内容(最新的暂存) 
git stash apply stash@{num} #取出指定的暂存 未删除暂存内的内容 
git stash drop #删除最新的暂存 
git stash drop stash@{num} #删除指定的暂存 
git stash clear #删除全部暂存

暂存区如图:

i-11.png

代码合并

#合并远程分支代码到当前分支(feature)
#方法一
git merge master #合并最新master代码 PS:代码合并前需要拉取远程最新的master代码
 
#方法二
git merge <remote-name>/<branch-name> 
#把远程分支合并到当前分支 git merge origin/master
 
#方法三
git pull <远程主机名> <远程分支名> #把远程分支合并到当前分支
#或
git pull --rebase <远程主机名> <远程分支名> #把远程分支合并到当前分支(rebase的模式)

PS: git pull = git fetch + git merge

1、--rebase

git pull 默认是merge 策略,在团队协作过程中,假设你和你的同伴在本地中分别有各自的新提交,而你的同伴先于你 push 了代码到远程分支上,那么你必须先执行 git pull 来获取同伴的提交,然后才能push 自己的提交到远程分支。而按照 Git 的默认策略,如果远程分支和本地分支之间的提交线图有分叉的话(既:--no-ff),Git 会执行一次 merge 操作,因此产生一次没意义的提交记录。 加上 --rebase 参数的作用是,提交线图有分叉的话,Git 会 rebase 策略来代替默认的 merge 策略,则不会产生那一次没意义的提交记录。

2、Fast Forward 和 no fast foward (--no-ff)

git pull 的 fast forward 方式是不会增加多余的提交记录的,如图:

i-12.png git pull 的 no fast forward (--no-ff)方式会增加多余的提交记录的,如图:

i-13.png

* git rebase 与 git merge

定义

rebase:变基,意思就是基于指定分支进行修改,base就是基于什么;

merge:合并,意思是把指定分支合并过来

区别

自己画了几张图总感觉不明确,费尽心思在网上找到这张图,感觉清晰易懂!!

i-14.png git merge 如图:

i-15.jpeg git rebase 如图:

i-16.png

git rebase的优点

1、整个commit提交成一条直线,commit信息清晰明了

2、去掉了不起作用的commit 避免干扰 方便查看

3、有利于代码回滚

git rebase的缺点

1、每次解决冲突都是一个一个合的,那是相当费劲啊,浪费时间还不方便

2、多人开发的分支不适合用,绝对不要在公共分支使用rebase

为什么绝对不要在公共分支使用rebase

网上大佬的说法,如图:链接

i-17.png

我的理解:

i-18.png 在master的基础上(代码为C1)开一个分支music_app(代码为C2, C2 == C1),然后master做了两次提交 C5、C6。

music_app同时有两个人开发小A和小B,这时我们本地最新的代码都是C2,小A做了两次提交 C3、C4,然后push做一个rebase, 那我变成了C3' , C4' 提交到了远程。

这时小B写了一个C7也就是想提交一下,那么小B面临的问题是: 底是rebase master的代码呢? 还是rebase 小A这个分支的代码也就是C4' 。

如果小B rebase的是master 那么小B与小A的代码就相差越来越多, 如果小B rebase的是小A C4' 的代码, 那么他面临的将是非常多的冲突。并且关键的关键在于,不去查看远程提交小B根本不知道到底rebase 哪一个。

Tag相关命令

git tag #列出所有的标签 
git tag -l "*202106*" #模糊搜索 
git tag -l -n #列出tag及注释 
git tag XXX #创建本地tag 
git tag -a XXXX -m '备注信息' #创建包含备注信息 
git show tag名称 #查看相关tag信息 
git push origin --tags #推送全部tag标签到远程仓库 
git tag -d tag名 #删除本地标签

冲突解决

#解决冲突的流程 (rebase模式)
git add .
git commit -m 'xx' 
git pull origin develop --rebase 
#解决冲突
git add .
git rebase --continue
#PS:如果冲突没有解决完 git add . 和 git rebase --continue 需要一直循环执行
git status # 只有在working tree clean的状态才能提交 
git push origin feature –f
#解决冲突的流程 (merge模式)
git checkout dev-test #把xxx分支合并到dev-test分支
git pull
git merge xxx
#解决冲突
git add . 
git commit -m 'xxxxxxx'
git push
#git rebase
--continue: 就是继续rebase
--abort: 表示放弃之前的rebase
--skip 就是跳过这个rebase

#ps: 比如dev-prod本地很久没有拉取远程的最新代码了,突然拉取以后发现又100个冲突,那么就可以跳过这次冲突,然后删除本地的dev-prod,再从远程重新拉取,避免解决100个冲突。

终端结合VScode

#解决冲突的流程 (merge模式) 
git checkout dev-test #把xxx分支合并到dev-test分支 
git pull 
git merge xxx

VScode中首先解决冲突,然后点击“+”好添加

i-19.png

#VScode解决冲突完毕以后 
git add . 
git commit -m 'xxxxxxx' 
git push

提交回退

如果提交了一次代码,内容很多,然后你发现提错分支了,那么你需要撤销怎么办?

git log #显示所有提交过的版本信息
git reset --hard 版本号 #回退到你要恢复的那一次提交

回退完以后你又发现那一大堆代码又需要了,但是你git log 已经找不到了,怎么办?

git reflog #可以查看所有分支的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作)
git reset --hard 版本号 #回退到你要恢复的那一次提交

命令配置

#git命令简化配置
git config --global alias.st status
git config --global alias.ci commit
git config --global alias.co checkout
git config --global alias.br branch
...

Git提交信息规范化

type(scope):subject

1、type:用于说明commit的类别,规定为如下几种

  • feat:新增功能;
  • fix:修复bug;
  • docs:修改文档;
  • refactor:代码重构,未新增任何功能和修复任何bug;
  • build:改变构建流程,新增依赖库、工具等(例如webpack修改);
  • style:仅仅修改了空格、缩进等,不改变代码逻辑;
  • perf:改善性能和体现的修改;
  • chore:非src和test的修改;
  • test:测试用例的修改;
  • ci:自动化流程配置修改;
  • revert:回滚到上一个版本; 2、scope:【可选】用于说明commit的影响范围

3、subject:commit的简要说明,尽量简短

为什么要用命令行?

图形化工具操作Git,本质上其实都是用的Git命令行,一些复杂的操作都是直接将GIT组合好后直接执行,只是软件把他们封装了起来,没让我们看到命令行而已。

我之前也是用图形化工具来使用Git命令行的操作,后来发现总是有着莫名其妙的问题,并且错误提示看起来就是一头雾水,每次看到错误都是复制出来然后去翻译或者百度,最后在去找解决办法。

后来看到大佬们用命令行提交代码,错误提示明了易懂,要干啥就输入啥提交准确,也不会出现那些莫名其妙的问题。

因为图形化工具是把一系列的Git命令给封装起来,而我们自己使用的时候,Git提交的逻辑顺序我们是很清楚的,这样一步一步走下来,只要逻辑是对的,就不会出错,就算出错了,命令行操作时,错在哪,该怎么修正,都提示的一清二楚,这也省下了我们拿着界面化软件的报错去找百度的时间。

刚开始用命令行操作的时候,是有一些不大习惯,但是用熟练之后,你完全就不会想打开图形化工具了

更何况编程过程中唯一能有点能像电影黑客的样子,也就是用Git命令行的时候了。毕竟每次用命令时,屏幕上的终端里代码一顿咔咔咔的跳着,感觉自己就像电影里的那些黑客一样:神秘的一批~ 帅气的一批~