针对 Git
最基本的命令的详细说明 ⑅︎◡̈︎*
-
写在最前:
若想直接了解关于
Git
在私有仓库和公共仓库中的使用命令,请 点击这里 直达结论。
Git
最基本的命令
Git
最基本的命令之前的文章中我对 Git
中最基础的几类命令有过形象地解释,这里再 po 一次:
我在本地电脑的
A文件夹
下修改某文件中的一行代码,这个修改信息就被 git 自动发现了,它把这个信息记在自己的小本子上,我们可以把每次被修改过的A文件夹整体
看作是一个新的犯人,使用git add
把这个犯人放到缓存区里暂时拘留,再通过git commit
把这些文件放到本地的仓库正式坐牢,而仓库里储存了每个犯人的犯罪信息(我自定义的信息),通过push
可以把仓库里的犯人同步到远程仓库,这时候远程仓库和本地仓库的信息就一样了。
Git
中最基础的命令就是 clone
、remote
、pull/fetch
、add
、commit
和 push
,一句话概括各个命令的作用:
clone
: 下载远程仓库remote
: 管理远程仓库pull/fetch
: 将远程仓库拉取(同步)到本地仓库add
: 本地的修改内容放到本地缓存区commit
: 缓存区内容添加进本地仓库push
: 本地仓库推送(同步)到远程仓库
对于一个项目而言,如何在本地电脑上从无到有,如何和远程仓库建立连接,提交自己的成功,有如下七步流程:
在本地目录中建立 Git 仓库 → 编辑本地目录的内容 → 将本地修改或新增内容加入到本地缓存区 → 本地缓存区内容添加进本地仓库 → 在本地仓库关联远程仓库 → 本地仓库推送到远程仓库 → 从远程仓库下拉更新其他人更改的内容到本地仓库
接下来我们针对每一步进行详细的介绍,Git 中最基础的命令如何在过程中使用。
获取 Git
仓库
Git
仓库通常来说,我们有两种获取 Git 项目仓库的方式:
这两种方式都会在你的本地电脑上得到一个就绪的 Git 仓库。
-
克隆(
git clone
)远程仓库
如果你想获得一份已经存在了的 Git 仓库的拷贝,比如说,你想为某个开源项目贡献自己的一份力,这时就要用到 git clone
命令。Git 克隆的是该 Git 仓库服务器上的几乎所有数据,当你执行 git clone
命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来(这也就意味着,如果你的服务器的磁盘坏掉了,你通常可以使用任何一个克隆下来的用户端来重建服务器上的仓库)。
克隆仓库的命令是 git clone <url>
。 比如,要克隆我的某个仓库,可以复制该仓库的地址,并使用下面的命令:
$ git clone https://github.com/CaymanHK/repository_name.git # .git 其实要不要加上都行
这会在终端运行的 当前目录下
创建一个名为 repository_name
的目录,并在这个目录下初始化一个 .git
文件夹, 从远程仓库拉取下所有数据放入 .git
文件夹,然后从中读取最新版本的文件的拷贝。 如果你进入到这个新建的 repository_name
文件夹,你会发现所有的项目文件已经在里面了。
如果你不想要本地仓库的名字叫做 repository_name
,你希望叫做 cayman_edit
,命令如下:
$ git clone https://github.com/CaymanHK/repository_name.git cayman_edit # 通过额外的参数指定本地仓库名字
Git 支持多种数据传输协议,上面的叙述使用的是 https://
协议,不过你也可以使用 git://
协议或者使用 SSH
传输协议,比如 user@server:path/to/repo.git
,以后将会对这三种协议进行详细的叙述。
-
在现有本地目录中初始化(
git init
)本地仓库
如果你有一个尚未进行版本控制的项目目录,想要用 Git 来管理或者控制,那么首先需要进入该项目目录中:
$ cd /Users/user/cayman_project # 进入现有的本地目录
通过以下命令在 /Users/user/cayman_project
目录中初始化一个本地仓库:
$ git init
该命令将创建一个名为 .git
的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是 Git 仓库的骨干。 但是在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪(关于跟踪一项,之后会专门写文章介绍),接下来就是追踪项目文件,或者说将本地修改或新增内容加入到本地缓存区( git add
)。
git add
git add
git add
命令的作用是将我们需要提交的代码从本地的工作区添加到缓存区,具体命令如下:
# 1. add 新添加和修改的文件到缓存区,但是不包括删除的文件
$ git add . # . 表示当前目录
# 2. add 修改和删除的文件到缓存区,但是不包括新添加的文件
$ git add -u # u 指的是 update ,对已有记录(已跟踪的文件)进行更新
$ git add --update # 和 -u 相同作用
# 3. add 所有的变动到缓存区
$ git add -A # A 就是 all 的意思
$ git add --all # 和 -A 相同作用
git add 通过
git add <files/directories>
命令进行文件追踪,如果参数是目录的路径,该命令将递归地跟踪该目录下的所有文件,所以我们也可以对单个文件进行跟踪,但是很明显一般情况使用不到。
接下来就是将本地缓存区内容添加进本地仓库。
git commit
git commit
git commit
主要是将暂存区里的改动给提交到本地的版本库。每次使用 git commit 命令我们都会在本地版本库生成一个 40 位的哈希值,这个哈希值也叫 commit-id
,commit-id 在版本回退的时候非常有用,它相当于一个快照,可以在未来的任何时候通过与 git reset
的组合命令回退到指定版本。
关于 git commit
具体用法如下:
- 将缓存区的文件提交到本地仓库
$ git commit -m "提交信息记录" # 提交信息会显示在远程仓库上
- ⚠️ 不建议的用法 ⚠️
$ git commit -a -m “提交信息记录”
$ git commit –am “提交信息记录” # 两者效果相同
-a
参数可以将所有已跟踪文件中的执行修改或删除操作的文件(不包括新添加的文件)都提交到本地仓库,即使它们没有经过 git add 添加到暂存区,该用法相当于先执行 git add -u
再执行 git commit -m "提交信息记录"
,不建议这么使用。
- 提交内容和信息的修改
$ git commit --amend
在终端的当前工作目录下输入以上命令会进入文本编辑界面,在该界面可以进行文件的追加提交或者提交信息的修改(毕竟手残党经常会手抖打出一些奇奇怪怪的信息是吧),通过该命令可以在不增加一个新的 commit-id
的情况下将新修改的代码或者提交信息追加到前一次的 commit-id
中。
- 查看帮助
$ git commit --help
ps. 日常开发并不需要了解
git commit --help
中的内容。
commit
完后就是在本地仓库关联远程仓库。
git remote
git remote
到目前为止我们的本地仓库已经建立完成,并且仓库中的内容准备好上传,但是在上传前还有最后一个问题:我们要把东西推送到哪里?
在第一次推送本地仓库前,我们得在当前的工作目录下设置远程仓库的地址,命令如下:
git remote add <shortname> <url>
$ git remote add origin https://github.com/CaymanHK/China.git # .git 加不加都行
$ git remote add cn https://github.com/CaymanHK/China.git # .git 加不加都行
以上两行唯一的区别就是
<shortname>
,其中cn
是后面整个 URL 的自定义简称,但是一般 Git 默认使用origin
,因为origin
这个名字一看就知道是远程仓库。
关于
url
中的协议,这里使用的是https://
协议,你也可以使用git://
协议或者使用SSH
传输协议,上文对此有进行过阐述。
我们可以查看关联的远程仓库的相关信息:
$ git remote # 查看关联的远程仓库的名称
$ git remote -v # 查看关联的远程仓库的详细信息
如果要修改远程仓库的地址,有如下两种方法:
- 先删除再添加:
$ git remote remove <shortname> # 通过 git remote 查看远程仓库名称 ( shortname )
$ git remote add origin <url>
- 直接修改:
$ git remote set-url origin <new_url>
关联完远程仓库后,就是将本地版本库的分支推送到远程服务器上对应的分支。
git push
git push
git push
的命令格式是:
git push <远程主机名> <本地分支名>:<远程分支名>
这里的 <远程主机名>
指的就是上一节所说的 <shortname>
,直观解释就是把本地仓库的某分支推送到远程仓库的某分支,一般而言我们把本地仓库和远程仓库分支都设为同名 master
,因此命令如下:
- 第一次推送分支
由于远程库是空的,我们第一次推送 master
分支时,加上 -u
参数,Git 不但会把本地仓库的 master
分支内容推送的远程仓库的 master
分支,还会把本地的 master
分支和远程的 master
分支关联起来,在以后的推送或者拉取时就可以简化命令。
$ git push -u origin master
- 平常使用的命令:
如果远程分支被省略,则表示将本地分支推送到与之存在追踪关系的远程分支:
$ git push origin master
- 更简化的命令:
如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略:
$ git push origin # 将本地当前分支推送到origin主机的对应分支
如果当前分支只有一个远程的追踪分支,那么主机名都可以省略:
$ git push
- ⚠️ 非常危险的用法,慎用 ⚠️
$ git push --force
$ git push -f
$ git push --force origin
$ git push origin master --force
$ git push origin master -f
$ git push -f origin master
这几种用法效果一样,当然我不是在告诉你这样用有多好,网上输入该代码随便一查都是警告和惨案…据说美国2018年有程序员因为用了这个命令发生了枪击案…
如果远程仓库的版本比本地版本更新,推送时 Git 会报错,要求先在本地做
git pull
合并差异,然后再推送到远程主机,这是正常合理的代码提交流程。在这时候使用--force
会将本地分支的提交强制覆盖远端推送分支的提交。也就是说,如果其他人在相同的分支推送了新的提交,你的这一举动将删除他的提交内容。因此应避免在公有的远程仓库使用这样的命令,当然对于个人私有的仓库就无所谓了。
针对这个用法,今天我发现本地某仓库的 .git
文件太大了,因此删除了该文件,并重新生成该文件,这意味着本地仓库的所有提交记录都被删除了,同时我在本地进行了不少的代码改动,因此使用 push origin master -f
强制推送到 GitHub 的远程仓库的时候发现,远程仓库的提交记录已被清空,原本几十个 commit
信息现在只剩下一个。
当他人对远程仓库的代码进行了更新之后,我们就需要从远程仓库下拉更新其他人更改的内容到本地仓库,拉取远程仓库的代码方式有两种:
git fetch
git fetch
在作用上 git fetch
和 git pull
的功能是大致相同的,都是起到了更新代码的作用,区别如下:
-
git fetch
是将远程仓库的最新内容拉到本地并记录在.git/FETCH_HEAD
中,用户在检查了以后自行决定是否合并到本地分支中。 -
git pull
基于本地的FETCH_HEAD
记录,比对本地的FETCH_HEAD
与远程仓库的版本号,然后git fetch
获得当前的远程分支的后续版本的数据,然后利用git merge
将其与本地的分支合并,可以认为是git pull
是git fetch
和git merge
两个步骤的合并。
我们可以借助下图来加深理解这两者的区别:
git fetch
和 git pull
类似,这里不再赘述,仅作简单的解释,其命令格式如下(这时候 <远程分支名>
在前面):
git fetch <远程主机名> <远程分支名>:<本地分支名>
对于只有一个远程仓库甚至远程仓库只有一个 master
远程分支的而言,我们可以使用如下命令:
$ git fetch origin master # 将 master 分支下载到本地仓库当前分支
# 甚至可以更简化
$ git fetch
在比较本地仓库的 master
分支和远程仓库的 master
分支时,有如下两种命令:
- 直接对比合并
$ git fetch origin master # 将远程仓库的 master 分支下载到本地当前 branch 中
# $ git log -p FETCH_HEAD # 查看从远程仓库取回的更新信息(可以不查看)
$ git log -p master ..origin/master # 比较本地的 master 分支和 origin/master 分支的差别(关于 .. 的作用我也不清楚)
$ git merge origin/master # 进行合并
- 本地创建暂时的分支
temp
$ git fetch origin master:temp # 在本地仓库新建一个 temp 分支,并将远程 origin 仓库的 master 分支代码下载到本地 temp 分支
# git diff <local branch> <remote>/<remote branch>
$ git diff temp # 比较本地仓库代码与远程仓库代码的区别
$ git merge temp # 将 temp 分支合并到本地 master 分支
$ git branch -d temp # 如果不想保留 temp 分支,可以将其删除
git pull
git pull
实际的 git pull
过程可以理解为:
$ git fetch origin master # 将远端的 master 分支拉取最新内容
$ git merge FETCH_HEAD # 将拉取的最新内容与当前分支合并
git pull
也和 git push
类似,格式如下:
git pull <远程主机名> <远程分支名>:<本地分支名>
git pull
合并后可能会出现冲突,需要手动解决冲突,错误提示如下:
# 更新的代码与本地的修改代码有冲突,先提交你的改变或者先将本地修改暂存起来
error: Your local changes to the following files would be overwritten by merge:
Please commit your changes or stash them before you merge.
关于如何解决冲突,将会在以后的文章中进行详细的介绍。
用
git pull
更新代码的话比较简单粗暴,但是根据commit-id
来看,它们的实现原理并不相同。在实际使用中,git fetch
更安全一些,因为在git merge
之前,我们可以查看代码的更新情况,再决定是否进行合并。
总结
前面碎碎念把每一类基本命令都详细地介绍了一番,现在用最简单的话总结一下:
- 自用的私有仓库
$ cd /Users/user/cayman_project # 进入现有的本地目录
$ git init # 第一次使用要初始化一个本地仓库
$ git remote add origin https://github.com/CaymanHK/China.git # 第一次使用要关联远程仓库地址
$ git add -A # 把所有变化加入缓存区
$ git commit -m "自定义提示信息" # 提交到本地仓库
$ git push origin master # 推送到远程仓库 origin 的 master 分支
- 公有仓库
$ cd /Users/user/cayman_project # 进入现有的本地目录
$ git clone https://github.com/CaymanHK/repository_name.git # 第一次使用要把远程仓库的内容克隆到本地
# 上传
$ git add -A # 把所有变化加入缓存区
$ git commit -m "自定义提示信息" # 提交到本地仓库
$ git push origin master # 推送到远程仓库 origin 的 master 分支
#下拉
$ git fetch orgin master # 将远程仓库的 master 分支下载到本地当前 branch 中
$ git merge origin/master # 进行合并