Git常用操作清单
Git简介
Git是目前世界上最先进的分布式版本控制系统,是为版本管理而设计的软件。由Linux的创始人Linus在2005年开发了Git的原型程序。
版本管理就是管理更新的历史记录,它为我们提供了一些在软件开发过程中必不可少的功能,例如记录一款软件添加或更改源代码的过程,回滚到特定阶段,恢复误删除的文件等。
版本控制系统的优势
你一定有这样的经历,在写文档时,想删除一个段落,又怕将来想恢复找不回来,你只好将这个文档存为副本,再接着改,改到一定程度,再继续存为副本,最后你的文件夹变成了下面这个样子。过了一周,你想找回被删除的文字,但是已经记不清楚保存在哪个文件里了,只好一个一个文件去找,非常繁琐。
若这是需要同事协作的任务,你复制一份给他,他将他的部分完成后,你必须想想,在他改动文件的期间,你作了哪些改动,然后才能将这两个文件合并,非常麻烦。如果这是一个十几人协作的项目呢?
若这是一个游戏项目,项目的源文件十分庞大,你生怕改动代码的时候触发到什么无法回退的bug,便复制一份源代码作为备份,再接着改,又继续保存副本。往往这个项目不只有你一个人写代码,这样的管理方式,不仅十分浪费空间,也是很糟糕的体验。
这时候版本控制系统的优势就显现出来了。
版本 | 文件名 | 用户 | 说明 |
---|---|---|---|
1 | readme.txt | Never | add git stage |
2 | readme.txt | Rory | append GPL |
3 | readme.txt | Rory | add distributed |
4 | readme.txt | Never | wrote a readme file |
不仅可以协作编辑,也不用手动管理,如果想看某次改动,只要将改动列表调出来查看就好了,并且若你想回退到任意一个版本都是可以的,这就是版本控制。
分布式与集中式
集中式的版本控制系统主要以CSV与SVN为主,将所有数据集中存放在中央服务器中,但是一旦开发者所处的环境不能连接服务器,就无法获取最新的源代码,开发也就几乎无法进行,而且万一服务器故障导致数据消失,开发者就再也见不到源代码了。
分布式版本控制系统没有中央服务器,每个人的电脑上都是一个完整的版本库,这样,工作的时候就不需要联网了,多人协作只需要把各自的修改推送给对方,就可以看到对方的修改了。
Git的工作原理与流程
workspace:工作区
Index/Stage:暂存区
Repository:仓库区(或本地仓库)
Remote:远程仓库
安装Git——MacOS
在 Appstore中下载Xcode,Xcode集成了Git,默认会进行安装。
然后在终端输入$ git,检查是否安装成功,若安装成功将显示如下git的基本信息。
SSH key 配置
以Github为例,本地Git仓库和GitHub仓库之间的传输是通过SSH加密的。为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。
- 首先在正式使用Git前需要配置用户名和邮箱
git config --global user.name "姓名"
git config --global user.email "邮箱地址"
- 生成SSH key
首先在你的用户主目录下查看是否有.ssh文件夹,若没有则需要手动生成SSH key。
ssh-keygen -t rsa -C "邮箱地址"
在主目录下则会生成如下文件,其中id_rsa.pub为公钥。
- 在GitHub上设置SSH key
打开github个人账户,在setting中找到SSH keys设置,点击创建一个新的key。
将我们共钥id_rsa.pub里的所有内容复制到key中,点击添加,便配置好该SSH keys了。
.gitignore文件配置
.gitignore简介
我们做的每个 Git 项目中都需要一个 .gitignore 文件,这个文件的作用就是告诉 Git 哪些文件不需要添加到版本管理中。比如我们项目中的 npm 包 (node_modules),它在我们项目中是很重要的,但是它占的内存也是很大的,所以一般我们用 Git 管理的时候是不需要添加 npm 包的。
常用的规则
- 以斜杠 / 开头表示目录;
- 以星号 * 通配多个字符;
- 以问号 ? 通配单个字符;
- 以方括号 [ ] 包含单个字符的匹配列表;
- 以叹号 ! 表示不忽略(跟踪)匹配到的文件或目录;
git 对于 .gitignore 配置文件是按行从上到下进行规则匹配的,意味着如果前面的规则匹配的范围更大,则后面的规则将不会生效;
操作
# dependencies npm包文件
/node_modules
# production 打包文件
/build
# misc
.DS_Store
npm-debug.log*
.DS_Store: 这个文件是 Mac OS X 用来存储文件夹的一些诸如自定义图标,ICON 位置尺寸,窗口位置,显示列表种类以及一些像窗体自定义背景样式,颜色这样的元信息。默认情况下,Mac OS X 下的每个文件夹下应该都会生成一个,包括网络介质存储盘和U盘这样的外部设备。
npm-debug.log: 项目主目录下总是会出现这个文件,而且不止一个,原因是 npm i 的时候,如果报错,就会增加一个此文件来显示报错信息,npm install 的时候则不会出现。
我们通过 vim .gitignore 在项目根目录创建一个 .gitignore 文件,先不往这个文件写入任何信息,此时查看状态会有未追踪文件 .DS_Store。
我们对 .gitignore 文件进行编辑:
.DS_Store
可以看到再次 git status 时,该文件就被忽略了。
如果在创建 .gitignore 文件之前就 push 了项目,那么即使在 .gitignore 文件中写入新的过滤规则,这些规则也不会起作用,Git 仍然会对所有文件进行版本管理,因为 Git 已经开始管理这些文件了,所以你无法再通过过滤规则过滤它们。
常用操作一览表
操作类别 | 操作指令 | |
---|---|---|
创建版本库 | $ git init | 在当前目录创建一个本地仓库 |
$ git clone <url> | 克隆远程仓库到本地 | |
本地操作 | $ git add | 向暂存区中添加<file>文件的所有修改 |
$ git add . | 向暂存区中添加所有文件(tracked & untracked)的部分修改(modifield & new) | |
$ git add -u | 向暂存区中添加已追踪文件(tracked)的所有修改(modifield & deleted) | |
$ git add -A | 向暂存区中添加所有文件(tracked & untracked)的所有修改(modifield & new & deleted) | |
$ git status | 查看仓库的状态 | |
$ git commit -m | 将暂存区的所有内容提交,保存仓库的历史记录 | |
$ git diff | 查看所有文件(或指定文件)在工作区和最新提交的差别 | |
$ git log | 查看所有文件(或指定文件)的历史提交记录——按时间顺序从新到旧 | |
$ git log --pretty=oneline | 查看所有文件(或指定文件)的历史提交记录,并在一行显示 | |
$ git log -p | 查看所有文件(或指定文件)的历史提交记录并显示每次提交的内容差异 | |
$ git log --graph | 查看所有文件(或指定文件)的图形化历史提交记录 | |
$ git reflog | 查看所有分支(或与指定文件相关)的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作) | |
分支的操作 | $ git branch | 查看本地仓库的所有分支 |
$ git branch -av | 查看本地仓库与远程仓库的所有分支与详情 | |
$ git branch <branch_name> </kbd> | 在本地仓库创建名<branch_name>的分支 | |
$ git branch -d <branch_name> | 在本地仓库删除名为<branch_name>的分支 | |
$ git checkout/switch <branch_name> | 将当前分支切换为<branch_name> | |
$ git checkout -b <branch_name> 或$ git switch -c <branch_name> | 在本地仓库创建名为<branch_name>的分支,并切换到该分支 | |
$ git checkout -b <l_branch> origin/<r_branch> 或 $ git switch -c <l_branch> origin/<r_branch> | 创建并切换到<l_branch>分支,将远程仓库的分支<r_branch>拉取到本地仓库该分支 | |
$ git merge <branch_name> | 将分支<branch_name>合并到当前分支,默认用fast forward模式合并 | |
$ git merge --no-ff -m <message> <branch_name> | 将分支<branch_name>合并到当前分支,禁用fast foward模式,保留分支信息 | |
连接远程仓库 | $ git remote add <remote> <url> | 连接远程仓库<url>,并命名为<remote> |
推送至远程仓库 | $ git push -u <remote> <branch> | 将当前分支的内容推送至 <remote> 仓库的 <branch> 分支,并设置默认 |
$ git push | 将当前分支的内容推送至 <remote> 仓库的 <branch> 分支(有upstream分支时可以省略) | |
从远程仓库拉取 | $ git pull | 将 <remote> 仓库的 <branch> 分支的内容拉取到当前分支(有upstream分支时可以省略) |
撤销与重做 | $ git reset --hard <commit> | 重置暂存区,将工作区的内容恢复成 <commit> 版本( <commit> 不填,默认为最新一次提交 HEAD) |
$ git reset --hard origin/master | 针对已提交但未推送的修改,从远程仓库把代码取回来,退回到还未 git push 前的最后一个版本 | |
$ git reset HEAD | 撤销暂存区,针对添加到暂存区但未提交的修改,工作区不改变,撤销暂存区所有文件(或指定文件) | |
$ git checkout —— <file> 或 $ git checkout <file> | 撤销本地修改,针对本地修改,工作区中文件 <file> 撤销修改 | |
$ git checkout . | 撤销本地修改,针对本地修改,工作区中所有修改的文件撤销修改 | |
$ git checkout <commit> . | 重置暂存区,将工作区的所有内容恢复成 <commit> 版本,注意和 reset --hard 的区别 | |
$ git checkout <commit> <file> | 重置暂存区,将工作区 <file> 文件的内容恢复成 <commit> 版本 | |
$ git fetch origin <branch> | 将远程仓库的 <branch> 分支拉取内容到本地仓库,但不进行合并到工作区 |
常用操作介绍
创建版本库
git init
$ git init
在当前目录创建一个本地仓库
如果初始化成功,当前目录下就会生成 .git 目录(一般为隐藏文件),这个 .git 目录里存储着管理当前目录内容所需的仓库数据。
git clone
$ git clone <url>
克隆远程仓库到本地
git clone 默认克隆的是这个仓库的 master 分支,如果需要获取其他远程分支需要用到 git pull 和 git checkout 语句。
本地操作
git add
向暂存区中添加文件
$ git add <file>
向暂存区中添加<file>文件的所有修改
$ git add .
向暂存区中添加所有文件(tracked & untracked)的部分修改(modifield & new)
$ git add -u
向暂存区中添加已追踪文件(tracked)的所有修改(modifield & deleted)
$ git add -A
向暂存区中添加所有文件(tracked & untracked)的所有修改(modifield & new & deleted)
git status
$ git status
查看仓库的状态
On branch master:显示当前分支
Changes not staged for commit:还未放入暂存取的修改
Changes to be committed:可提交的修改
Untracked files:未追踪的文件
git commit
$ git commit -m <message>
将暂存区的所有内容提交,保存仓库的历史记录
git commit --amend -m <new message>
修改最近一次提交的提交信息
git diff
$ git diff <file>1
查看所有文件(或指定文件)在工作区和暂存区的差别
若我们将test.txt的修改提交到暂存区,这时test.txt工作区与暂存区无区别。
$ git diff HEAD <file>
查看所有文件(或指定文件)在工作区和最新提交的差别
尽管我们将test.txt的修改放入了暂存区,但我们还未提交修改,因此可以看到test.txt的修改。
查看历史记录
git log
查看当前状态为终点的历史提交记录
$ git log <file>
查看所有文件(或指定文件)的历史提交记录——按时间顺序从新到旧
$ git log --pretty=oneline <file>
查看所有文件(或指定文件)的历史提交记录,并在一行显示
$ git log -p <file>
查看所有文件(或指定文件)的历史提交记录并显示每次提交的内容差异
$ git log --graph <file>
查看所有文件(或指定文件)的图形化历史提交记录
git reflog
$ git reflog <file>
查看所有分支(或与指定文件相关)的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作)
例如我们回退一个版本后,git log 语句显示的是以当前状态为终点的提交历史记录,若我们已将上面的命令窗口关闭,此时要取消该步回退,就找不到原来版本的 commit_id 了。
通过 git reflog 语句我们可以查看每一步操作,以及 commit_id。
分支的操作
在进行多个并行作业时,我们会用到分支,在这类并行开发的过程中,往往同时存在多个最新代码的状态,如下图,从 master 分支创建分支 Never 和 Rory 后,每个分支中都拥有自己的新代码。master 分支是 Git 默认创建的分支,因此基本上所有的开发都是以这个分支为中心进行的。
不同的分支,可以同时进行完全不同的作业,等该分支作业完成之后再与 maste r合并,如下图,Never 分支的作业结束后与 master 合并。
灵活运用分支,可以让多人同时高效的进行并行开发。
git branch
$ git branch
查看本地仓库的所有分支
$ git branch -av
查看本地仓库与远程仓库的所有分支与详情
a:所有分支 / v:显示详情
$ git branch <branch_name>
在本地仓库创建名为<branch_name>的分支
$ git branch -d <branch_name>
在本地仓库删除名为<branch_name>的分支
git checkout
$ git checkout/switch <branch_name>
将当前分支切换为<branch_name>
$ git checkout -b <branch_name> 或
$ git switch -c <branch_name>
在本地仓库创建名为<branch_name>的分支,并切换到该分支
$ git checkout -b <l_branch> origin/<r_branch> 或
$ git switch -c <l_branch> origin/<r_branch>
创建并切换到<l_branch>分支,将远程仓库的分支<r_branch>拉取到本地仓库该分支
git merge
$ git merge <branch_name>
将分支<branch_name>合并到当前分支,默认用fast forward模式合并
当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。
首先在 master 主分支下创建分支 never 和 rory :
Rory 和 Never 在各自的分支下都进行了一些作业,并且提交:
将 rory 分支合并到 never 时,发现冲突:
合并产生冲突后的文件:
手动合并后,再进行提交:
$ git merge --no-ff -m <message> <branch_name>
将分支<branch_name>合并到当前分支,禁用fast foward模式,保留分支信息
连接远程仓库
git remote
$ git remote add <remote> <url>
连接远程仓库<url>,并命名为<remote>
连接远程仓库需要先配置SSH key。
推送至远程仓库
git push
$ git push -u <remote> <branch>
将当前分支的内容推送至 <remote> 仓库的 <branch> 分支,并设置默认
-u 参数将 <remote> 的 <branch> 分支设为本地仓库当前分支的 upstream;下次运行 git push 命令时,直接将当前分支推送至该 upstream;下次运行 git pul l命令从远程仓库获取内容时,直接从该 upstream 获取内容。
$ git push <remote> <branch>
将当前分支的内容推送至 <remote> 仓库的 <branch> 分支(有upstream分支时可以省略)
从远程仓库拉取
git pull
$ git pull <remote> <branch>
将 <remote> 仓库的 <branch> 分支的内容拉取到当前分支(有upstream分支时可以省略)
撤销与重做
git reset
$ git reset --hard <commit>
重置暂存区,将工作区的内容恢复成<commit>版本(<commit>不填,默认为最新一次提交)
对于 <commit>,除了版本号,还有一些快速写法,如用 HEAD 表示当前版本,上一个版本就是 HEAD^,上上一个版本就是 HEAD^^,若是往上100个版本则是 HEAD~100。
$ git reset --hard origin/master
针对已提交但未推送的修改,从远程仓库把代码取回来,退回到还未git push前的最后一个版本
其实与上一条语句一样,在这里 <commit> 版本号为 origin/master 指的是 push 前的最后一个版本,当然如果你查看历史,查出 push 前的最后一个版本号当然也可以。
$ git reset HEAD <file>
撤销暂存区
针对添加到暂存区但未提交的修改,工作区不改变,撤销暂存区所有文件(或指定文件)
git checkout
$ git checkout —— <file> 或
$ git checkout <file>
撤销本地修改
针对本地修改,工作区中文件 <file> 撤销修改
如下将最后一行从Hi都改为Hey:
可以看到工作区已经撤销修改
$ git checkout .
撤销本地修改
针对本地修改,工作区中所有修改的文件撤销修改
$ git checkout <commit> .
重置暂存区,将工作区的所有内容恢复成<commit>版本,注意和reset --hard的区别
如下图,这是 checkout 与 reset --hard 最明显的差别,使用 checkout 时 HEAD 指针不会移动,进行版本倒退后还可以版本前进。
而使用 reset --hard 时 HEAD 指针移动到回退版本,一旦回退,指针就无法再指回来了。
$ git checkout <commit> <file>
重置暂存区,将工作区<file>文件的内容恢复成<commit>版本
git fetch
$ git fetch origin <branch>
将远程仓库的<branch>分支拉取内容到本地仓库,但不进行合并到工作区
与pull的区别可以这样理解:git pull = git fetch + git merge
斜体删除线表示该命令是可选的 ↩︎