什么是 Git?

Git 是一个免费的开源分布式版本控制系统,它的设计目的是为了速度和效率的处理从小型到大型的项目;Git 可以帮我们管理我们的代码,记录历史,只要代码提交到 Git 上就永久不会丢失,可以随时 “穿越”(回到之前的某一个版本);可以多端共享,团队协作中,多个人操作了同一个文件时,可以实现自动合并(模块化,组件化)、标记冲突,拥有强大的分支管理系统。

Git 与 SVN 的区别

SVN:集中式,需要一台中央服务器,所有代码的拉取和提交都是在中央服务器,一旦中央服务器或者网络出现故障,则不能拉取和提交代码,需要不断去备份中央服务器,防止代码丢失。

Git:分布式,有一个中央服务器的同时,每个开发者本地都有自己的本地仓库,拥有完整的版本库,不用担心代码丢失,Git 存储的是代码变化的快照,更新代码的速度要比 SVN 更快。

Git 安装

Windows

Mac

常用 Linux 命令

查看当前工作目录

$ pwd

创建文件夹

$ mkdir 文件夹名

改变路径

# 进入盘符
$ cd d:
# 进入文件夹
$ cd 文件夹名
# 回上一级目录
$ cd ..
# 进入某一个路径
$ cd 路径

想要进入某一个不知道路径的文件夹中,可以直接拖拽该文件到命令窗口,会自动识别路径。

查看文件列表

$ ls
$ ls -a
$ ls -al

下面命令加了 -a 参数可以查看隐藏文件,加了 -al 参数可以查看所有文件及权限位。

创建文件

$ touch 文件名

移动文件

$ mv 文件/文件夹 路径

移动文件时可以通过第二个参数对文件进行重命名操作。

拷贝文件

$ cp 文件/文件夹 路径

将文件或者文件夹移动或拷贝到所输入的路径下。

查看文件内容

$ cat 文件名

删除文件 / 文件夹

$ rm -rf 文件夹名
$ rm 文件名

-rf 为递归删除,后面加上 * 为参数会递归删除整个文件夹的内容,rm -rf *(慎用)。

清空命令窗口

$ clear

查看命令历史

# 直接在命令行中查看
$ history
# 将当前 Git 的命令历史写入文件中
$ history > 文件名

使用 vi 编辑器编辑文件

$ vi 文件名
# 进入编辑模式(键盘)
i
# 进入命令模式(键盘)
Esc
# 保存并退出(键盘)
:wq
# 强制退出(键盘)
:q!

使用命令编辑文件

# 向文件输入内容
$ echo 内容 > 文件名
# 向文件追加内容
$ echo 内容 >> 文件名

当使用 echo 编辑了一个不存在的文件时,会创建一个新文件并将内容编辑到文件中,而 touch 创建的是空文件。

Git 的本地操作

  • Git 在管理文件时,所有文件都具有三种状态,已修改、已暂存、已提交。
  • Git 在本地仓库中由三部分组成,工作区、暂存区、版本库。
  • Git 管理的文件夹下都有一个名为 .git 的隐藏文件夹。

对应关系如下:

  • 已修改 → 工作区
  • 已暂存 → 暂存区(.git 文件夹下的 index 文件中)
  • 已提交 → 版本库

配置用户

不配置用户无法提交代码。

# 查看配置信息
$ git config --list
# 配置用户名
$ git config --global user.name '你的名字'
# 配置邮箱
$ git config --global user.email '你的邮箱'
# 查看某一项配置
$ git config --global user.name
$ git config --global user.email

配置用户信息参数:

  • --local:只对某一个仓库生效;
  • --global:对计算机当前用户所有仓库生效;
  • --system:对计算机整个操作系统生效。

初始化 Git 仓库

在要初始化的文件夹下执行下面命令,告诉 Git 哪个文件夹被 Git 所管理,一个项目初始化一次,不能嵌套。

# 把已有项目纳入 Git 管理
$ cd 项目代码所在文件夹
$ git init
# 新建项目直接用 Git 管理
$ cd 希望创建项目的文件夹
$ git init your_project #会在当前路径下创建和项目名称同名的文件夹
$ cd your_project

查看 Git 状态

$ git status

文件为红色,代表有修改,文件名为绿色,代表已经加入暂存区。

添加到暂存区

# 将单个文件变化提交到暂存区,参数支持多个
$ git add 文件名/文件夹
# 将已经被 Git 管理的文件变化全部提交
$ git add -u
# 将修改、添加文件的变化全部提交
$ git add .
# 将修改、添加、删除文件的变化全部提交
$ git add -A

删除暂存区

# 删除暂存区,工作区不保留,参数支持多个
$ git rm 文件名/文件夹
# 删除暂存区,工作区保留
$ git rm --cached 文件名
# 删除全部暂存区
$ git rm --cached . -r

当参数为 . 的时候删除全部暂存区,所以需要加上代表递归删除的参数 -r

重命名暂存区文件

# 方式一(不常用)
$ mv oldname newname
$ git rm oldname
$ git add newname
# 方式二
$ git mv oldname newname

当对一个已被 Git 管理的文件使用 mv 重命名时,执行 git status 命令时,会提示删除该文件(旧名字),新增该文件(新名字),使用 git rmgit add 命令进行删除和添加,执行 git status 命令时会提示该文件从 renamed: oldname -> newname,而 git mv 命令等于将上面三个步骤合并成一个。

注意:OS 系统中大小写不敏感,重命名后的文件名只是大小写改变,git mv 命令执行会失效。

提交到版本库

# 从暂存区提交到版本库
$ git commit -m '版本信息'
# 从工作区直接提交到版本库(需要之前添加过暂存区)
$ git commit -am '版本信息'

查看提交日志(版本库)

# 最详细的信息
$ git log
# 查看某一个分支的提交历史
$ git log 分支名
# 单行查看,只有版本号和提交信息
$ git log --online
# 查看最近 4 个提交,数字根据需要配置
$ git log -n4
# 查看所有分支的提交历史
$ git log --all
# 查看所有分支图形化的提交历史
$ git log --all --graph
# 查看全部版本记录,包含被删除的提交记录
$ git reflog
# 以列表的方式查看单个文件的提交
$ git blame 文件名
  • --oneline-n*n 可省略)、--all--graph 等参数可以组合使用;
  • logreflog 的区别在于 reflog 可以查看被删除的提交记录,当想回退被删除的提交时使用;
  • 查看图形化分支时,红色线代表主分支,绿色线代表新创建的分支,分支上的 * 代表提交到版本库的节点。

查看 Git 命令 Web 文档

$ git help --web 命令

暂存更改

分支工作区有更改不能直接切换其他分支,可以提交更改或者暂存更改,若暂存更改(使用暂存区覆盖掉工作区),等待重新切回分支时,还原暂存。
此处所说的暂存不是之前的将代码提交到暂存区,因为当前分支工作区的代码会变成要切换分支工作区的代码,而导致当前分支工作区的更改丢失,此处的暂存类似于将修改寄存,重新切回该分支时再还原。

# 暂存更改
$ git stash
# 查看暂存列表
$ git stash --list
# 还原暂存的内容
$ git stash apply|pop stash@{n}

applypop 都是用于还原暂存的内容到工作区,stash@{n} 代表还原指定的暂存,n 代表暂存的序号,若省略 stash@{n} 则代表默认取出最新的暂存,applypop 的区别是,apply 还原的暂存,在暂存栈中依然存在,可多次还原,pop 还原的暂存在覆盖到工作区同时,暂存栈中删除该暂存。

分支操作

查看分支

# 查看本地分支及分支最后一次提交信息
$ git branch
# 查看本地分支
$ git branch -v
# 查看所有分支
$ git branch -a
# 查看所有分支及最后一次提交信息
$ git branch -av

创建分支

# 从当前所在分支的 commit 创建分支
$ git branch 新分支名
# 基于已有分支的 commit 创建分支
$ git branch 新分支名 分支名

注意:

  • 新创建的分支和主分支 master 还是同一个区域,新建的文件只有提交到新分支的版本库才真正脱离关系;
  • Git 刚刚初始化管理的的文件夹必须有一次提交到版本库(root-commit:根提交)以后才会有主分支 master,否则即使创建了新分支也无法切换回 master

切换分支

# 切换分支
$ git checkout 分支名
# 创建并切换分支
$ git checkout -b 分支名

删除分支

# 删除已经合并的分支
$ git branch -d 分支名
# 删除未合并的分支
$ git branch -D 分支名

需切换出要删除的分支,才能进行删除操作,使用 -d 在删除前 Git 会判断在该分支上开发的功能是否被 merge 到其它分支,如果没有,不能删除,如果 merge 到其它分支,但之后又在其上做了开发,使用 -d 还是不能删除,-D 会强制删除。

合并分支

将指定分支合并到当前所在的分支,所以,在分支开发完毕后,合并分支需要先切换回目标分支。

$ git merge 指定的分支名

比较变更

# 两个不同分支的比较,文件名参数省略比较所有文件
$ git diff 分支1 分支2 文件名
# 两个不同的 commit 比较,文件名参数省略比较所有文件
$ git diff 61db01a 968adc7 文件名

commit 也可以用 HEAD 指代当前 HEAD 所在的提交,参数详情如下:

  • HEAD^:父节点;
  • HEAD^n:第 n 个父节点;
  • HEAD^^:父节点的父节点;
  • HEAD~:父节点;
  • HEAD~2:父节点的父节点。

HEAD^^ 等同于 HEAD~2,一个节点可以有多个子节点(在某个 commit 下创建多个分支),也可以有多个父节点(多个分支的 commit 合并)。

# 工作区和暂存区比较
$ git diff
# 工作区和版本库比较
$ git diff 分支名
# 暂存区和版本库比较
$ git diff --cached|--staged

撤销和回退操作

$ git checkout 文件名
$ git checkout .
# 撤销某一个版本的文件到工作区
$ git checkout 版本号 文件名

撤销操作是将暂存区覆盖到工作区,会放弃掉当前工作区修改的内容,. 参数是将整个暂存区覆盖当前工作区,一旦撤销就回不到之前的工作区了。
当不小心将当前工作区错误的代码提交到暂存区,可以使用下面命令将暂存区回滚到上一个暂存区,只可回滚一次。

# 文件名参数,省略后为撤销全部文件更改的暂存
$ git reset HEAD 文件名
# 按版本号回退版本
$ git reset --hard 版本号
# 回退到上一个版本
$ git reset --hard HEAD^

reset 指令的参数有三种,区别如下:

  • --mixed:默认参数,可省略,暂存区、版本库修改为指定的 commit 状态;
  • --soft:只将版本库修改为指定的 commit 状态;
  • --hard:工作区、暂存区、版本库都修改为指定的 commit 状态。

注意:当想要改变工作区内容时使用 checkout,当想要改变暂存区内容时使用 reset

解决冲突

当创建分支后,分支和 master 主分支分别提交代码到版本库,此时切换回 master 主分支,合并分支会出现冲突,需手动处理后,重新提交到暂存区并提交到版本库。

代码冲突:

<<<<<<< HEAD (当前更改)
master 分支代码
=======
开发分支的代码
>>>>>>> 开发的分支 (传入的更改)

Git 操作远程仓库

远程仓库可以是 GithubGitee(码云)、Coding 或者中央服务器等等。

以下是常用免费仓库的地址,可以在首页注册账号:

克隆仓库

克隆仓库带工作区

$ git clone 项目地址 项目别名

上面命令项目别名是可选的,相当于给项目根文件夹重命名。

克隆裸仓库

# 使用哑协议
$ pwd
# /Users/pandashen/Study/git_learning
$ git clone --bare /Users/pandashen/Study/git_learning/.git ya.git

# Cloning into bare repository 'ya.git'...
# done.
# 使用智能协议
$ git clone --bare fill:///Users/pandashen/Study/git_learning/.git zhineng.git

# Cloning into bare repository 'zhineng.git'...
# remote: Enumerating objects: 23, done.
# remote: Counting objects: 100% (23/23), done.
# remote: Compressing objects: 100% (18/18), done.
# remote: Total 23 (delta 2), reused 0 (delta 0)
# Receiving objects: 100% (23/23), done.
# Resolving deltas: 100% (2/2), done.

裸仓库是一个文件夹,内部存储的与带有工作区仓库的 .git 相同,使用哑协议和智能协议的区别是哑协议没有进度信息,而且智能协议由于对传输的信息进行了打包、压缩,所以传输速度更快。

关联远程仓库

# 创建关联
$ git remote add origin 远程仓库地址
# 查看关联的远程仓库
$ git remote -v
# 删除远程仓库的关联
$ git remote rm 地址别名

地址别名指的是上面的 origin,也可以是其他名称,必须对应要删除关联的地址别名。

处理需要提交时过滤的文件/文件夹

使用 WebStrom 编辑器编辑代码时,会自动在根目录生成 .idea 文件夹,使用 Mac 开发时根目录下的 .DS_Store 文件夹,以及在项目开发时会安装依赖存放在 node_modules 文件夹中,此类文件夹都是在把代码上传到远程仓库或中央服务器时不应该上传的,因此应该在上传之前过滤掉。

在根目录创建 .gitignore 文件用于记录上传时被忽略的文件夹,内容(可根据需要自行配置)如下:

# 文件:.gitignore
.idea
.DS_Store
node_modules

Git 上传时会自动忽略空文件夹,假设想要上传一个名为 public 的空文件夹,需要在文件夹内新建一个名为 .gitkeep 的文件(名字随意,最好有语义化),目的是使要提交的空文件夹不再为空。

推送代码到远程仓库

# 将本地所有分支推送到远端
$ git push 地址别名 --all
# 将本地分支推送到远端
$ git push 地址别名 分支名
$ git push -u 地址别名 分支名

如果加上了 -u 参数,以后再次提交时可省略地址别名和分支名称,直接执行下面命令进行提交。

$ git push
$ git push -f

注意:-f 参数是在 Git 默认不允许的情况下也能将代码推送到远程服务器,是一个非常危险的命令,团队中一般禁止使用。

拉取远程仓库的代码

在提交代码时,如果直接提交到远程仓库,会将当前代码覆盖到远程仓库,如果别人之前也向远程仓库提交了代码,会在远程仓库中造成冲突,所以一般在提交代码之前先拉取远程仓库的代码与本地代码进行合并,并产生一个新的历史记录,若出现冲突,手动处理冲突后再统一提交到远程仓库。

# 拉取但不合并代码(与 merge 配合,不常用)
$ git fetch 地址别名 分支名
# 拉取并合并代码(常用)
$ git pull origin master
# 拉去代码执行变基操作
$ git pull --rebase

创建并拉取远程仓库的分支

# 创建、切换并拉取远程分支
$ git checkout -b 分支名 地址别名/分支名

上面命令的意思是在本地创建并切换分支,同时将远端分支代码拉取到这个刚创建的分支。

创建 gh-pages 分支来发布静态页

涉及到远程仓库网站上的操作均以 Github 为例,其他仓库大同小异:

  • 在项目中创建一个静态页分支,我们使用 gh-pages 作为分支名;
  • gh-pages 分支提交到线上仓库;
  • 找到提供仓库网站的 Settings 设置,切换到 github-pages 分支;
  • 点击该栏顶端的地址可以访问我们的静态页。
# 创建静态页命令
$ git checkout -b gh-pages
$ touch index.html
$ git add .
$ git commit -m '提交信息'
$ git push origin gh-pages

向别人的项目提问

  • 在远程仓库网站进入别人的项目页面;
  • 点击 Issue 选项;
  • 输入问题标题和问题描述并点击提交;
  • 项目所有者可以回复或关闭问题。

更改别人的项目代码

  • 在别人项目的主页上有一个叉子的图标,操作名为 Fork
  • Fork 是在当前项目下克隆了一份,如果代码更新,不会随之更新;
  • 使用 clone 命令克隆自己的地址将项目拉到本地,进行操作;
  • 默认就是 Git 仓库而且有 origin 地址,修改后可以将代码提交到自己的仓库上;
  • 只有 Fork 关系才能修改别人代码后点击 New pull request 发送提交请求;
  • 点击 Create pull request 按钮,填写提交标题,和提交详情,确认提交;
  • 项目所有者可以在自己的项目页面中 Pull request 菜单中查看提交并处理;
  • 点击 Close pull request 关闭,点击 Merge pull request 同意提交并合并。

如果是一个团队的其他人需要操作同一个项目,上面的过程显得很繁琐,项目所有者可以在 SettingsCollaborators 选项中通过添加别人的账号或用户名向项目中添加贡献者,被添加的人拥有最大权限。

GUI 界面化

在当前的前端开发编辑器中,如 VSCodeWebStorm 等都集成了 Git,也有专门用于管理代码的软件 Sourcetree 等,可以直接点击按钮操作,不必使用命令行,这种操作 Git 的界面称作 GUI 界面,个人建议还是尽量少的使用 GUI 界面,命令行是根本,还是多敲命令,孰能生巧。