# 常用 Git 命令简介及使用

#### &#x20;博客作者：联系请[点击](https://hezhiqiang.gitbook.io/about-the-author/lian-xi-zuo-zhe)，搬运不易，希望请作者喝咖啡，可以点击[联系博客作者](https://hezhiqiang.gitbook.io/about-the-author/lian-xi-zuo-zhe)

## 一、Git背景

&#x20;[**Git**](http://book.git-scm.com/) 是一个分布式版本控制工具，它的作者 [Linus Torvalds](http://en.wikipedia.org/wiki/Linus_Torvalds) 是这样给我们介绍 [Git](http://en.wikipedia.org/wiki/Git_\(software\))  —— The stupid content tracker（傻瓜式的内容跟踪器）

Git 最初由Linus Torvalds编写，用于 Linux 内核开发的版本控制工具。

Git 与常用的版本控制工具 CVS、Subversion 等不同，它采用了分布式版本库的方式，不必服务器端软件支持，使源代码的发布和交流极其方便。

## 二、Git的诞生

很多人都知道，Linus在1991年创建了开源的Linux，从此，Linux系统不断发展，已经成为最大的服务器系统软件了。

Linus虽然创建了Linux，但Linux的壮大是靠全世界热心的志愿者参与的，这么多人在世界各地为Linux编写代码，那Linux的代码是如何管理的呢？

事实是，在2002年以前，世界各地的志愿者把源代码文件通过diff的方式发给Linus，然后由Linus本人通过手工方式合并代码！

你也许会想，为什么Linus不把Linux代码放到版本控制系统里呢？不是有CVS、SVN这些免费的版本控制系统吗？因为Linus坚定地反对CVS和SVN，这些集中式的版本控制系统不但速度慢，而且必须联网才能使用。有一些商用的版本控制系统，虽然比CVS、SVN好用，但那是付费的，和Linux的开源精神不符。

不过，到了2002年，Linux系统已经发展了十年了，代码库之大让Linus很难继续通过手工方式管理了，社区的弟兄们也对这种方式表达了强烈不满，于是Linus选择了一个商业的版本控制系统BitKeeper，BitKeeper的东家BitMover公司出于人道主义精神，授权Linux社区免费使用这个版本控制系统。

安定团结的大好局面在2005年就被打破了，原因是Linux社区牛人聚集，不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议（这么干的其实也不只他一个），被BitMover公司发现了（监控工作做得不错！），于是BitMover公司怒了，要收回Linux社区的免费使用权。

Linus可以向BitMover公司道个歉，保证以后严格管教弟兄们，嗯，这是不可能的。实际情况是这样的：

Linus花了两周时间自己用C写了一个分布式版本控制系统，这就是Git！一个月之内，Linux系统的源码已经由Git管理了！牛是怎么定义的呢？大家可以体会一下。

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

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

## 三、优点&#x20;

1\.   分布式开发时，可以git clone克隆一个本地版本，然后在本地进行操作提交，本地可以完成一个完整的版本控 制。在发布的时候，使用git push来推送到远程即可。&#x20;

2\.   git分支的本质是一个指向提交快照的指针，速度快、灵活，分支之间可以任意切换。都可以在本地进行操作可 以不同步到远程。&#x20;

3\.   冲突解决，多人开发很容易就会出现冲突，可以先pull远程到本地，然后在本地合并一下分支，解决好冲突，在 push到远程即可。&#x20;

4\.    离线工作，如果git服务器出现问题，也可以在本地进行切换分支的操作，等联网后再提交、合并等操作。&#x20;

## 四、缺点&#x20;

1\.    Git没有严格的权限控制，一般是通过系统设置文件的读写权限来做权限控制。&#x20;

2\.    工作目录只能是整个目录，而svn可以单独checkout某个有权限的目录。&#x20;

3\.     Git上手可能没有svn那边顺手，需要经过学习一下。

{% hint style="info" %}
**总结 :**

#### 如果对访问控制、权限分配和代码安全性等要求比较高的，建议使用svn。&#x20;

#### 如果是分布式，多人开发，版本迭代比较快的项目，建议使用Git。

{% endhint %}

## **五. 安装Git**

### 一、Linux – 打开终端，然后通过包管理安装

```bash
yum -y install git
# or: dnf -y install git
```

#### 在Ubuntu上命令是：

```bash
sudo apt-get install git
```

### 二、Windows – 推荐使用[git for windows](https://gitforwindows.org)，它包括了图形工具以及命令行模拟器

{% embed url="<https://gitforwindows.org/>" %}

### 三、OS X – 最简单的方式是使用[homebrew](https://brew.sh)安装，命令行执行brew install git

{% embed url="<https://brew.sh/>" %}

{% hint style="success" %}

#### 如果你是新手，推荐使用图形工具[Github desktop](https://desktop.github.com/)和[Sourcetree](https://www.atlassian.com/zh/software/sourcetree)。不过即使使用图形界面的应用，知道一些基本的 git命令依然很重要。接下来的内容我们集中在命令行控制上。

{% endhint %}

{% embed url="<https://www.atlassian.com/zh/software/sourcetree>" %}

{% embed url="<https://desktop.github.com>" %}

**如果你习惯使用了SVN的Windows桌面图形工具当然也可以下载小乌龟**

{% hint style="danger" %}

#### 注意：下载中文汉化包需要与下载的**Git**客户端相对应版本，安装好汉化包需要重启电脑才能生效。

{% endhint %}

{% embed url="<https://tortoisegit.org/download/>" %}

### **四、Git官网各种发行版下载：**

{% embed url="<https://git-scm.com/downloads>" %}

{% hint style="info" %}
Git是分布式的，这意味着它并不依赖于中心服务器来保存你文件的旧版本。

任何一台机器都可以有一个本地版本的 控制系统，其实就是一个硬盘上的文件，我们称之为仓库（repository）。如果是多人协作的话，你还需要一个线上 仓库，用来同步代码等信息。

这就是[GitHub](https://github.com/)、[BitBucket](https://bitbucket.org)等网站做的工作。
{% endhint %}

{% embed url="<https://github.com/>" %}

{% embed url="<https://bitbucket.org/product/>" %}

## 六. **Git 常用命令**

{% hint style="success" %}

### Git学习网站：

{% endhint %}

{% embed url="<https://learngitbranching.js.org/>" %}

### 常用 Git 命令清单。几个专用名词的译名如下。

```bash
Workspace：       #工作区
Index / Stage：   #暂存区
Repository：      #仓库区（或本地仓库）
Remote：          #远程仓库
```

### 一、新建代码库

> ```
> # 在当前目录新建一个Git代码库
> git init
>
> # 新建一个目录，将其初始化为Git代码库
> git init [project-name]
>
> # 下载一个项目和它的整个代码历史
> git clone [url]
> ```

### 二、配置

Git的设置文件为`.gitconfig`，它可以在用户主目录下（全局配置），也可以在项目目录下（项目配置）。

> ```
> # 显示当前的Git配置
> git config --list
>
> # 编辑Git配置文件
> git config -e [--global]
>
> # 设置提交代码时的用户信息
> git config [--global] user.name "[name]"
> git config [--global] user.email "[email address]"
> ```

### 三、增加/删除文件

> ```
> # 添加指定文件到暂存区
> git add [file1] [file2] ...
>
> # 添加指定目录到暂存区，包括子目录
> git add [dir]
>
> # 添加当前目录的所有文件到暂存区
> git add .
>
> # 添加每个变化前，都会要求确认
> # 对于同一个文件的多处变化，可以实现分次提交
> git add -p
>
> # 删除工作区文件，并且将这次删除放入暂存区
> git rm [file1] [file2] ...
>
> # 停止追踪指定文件，但该文件会保留在工作区
> git rm --cached [file]
>
> # 改名文件，并且将这个改名放入暂存区
> git mv [file-original] [file-renamed]
> ```

### 四、代码提交

> ```
> # 提交暂存区到仓库区
> git commit -m [message]
>
> # 提交暂存区的指定文件到仓库区
> git commit [file1] [file2] ... -m [message]
>
> # 提交工作区自上次commit之后的变化，直接到仓库区
> git commit -a
>
> # 提交时显示所有diff信息
> git commit -v
>
> # 使用一次新的commit，替代上一次提交
> # 如果代码没有任何新变化，则用来改写上一次commit的提交信息
> git commit --amend -m [message]
>
> # 重做上一次commit，并包括指定文件的新变化
> git commit --amend [file1] [file2] ...
> ```

### 五、分支

> ```
> # 列出所有本地分支
> git branch
>
> # 列出所有远程分支
> git branch -r
>
> # 列出所有本地分支和远程分支
> git branch -a
>
> # 新建一个分支，但依然停留在当前分支
> git branch [branch-name]
>
> # 新建一个分支，并切换到该分支
> git checkout -b [branch]
>
> # 新建一个分支，指向指定commit
> git branch [branch] [commit]
>
> # 新建一个分支，与指定的远程分支建立追踪关系
> git branch --track [branch] [remote-branch]
>
> # 切换到指定分支，并更新工作区
> git checkout [branch-name]
>
> # 切换到上一个分支
> git checkout -
>
> # 建立追踪关系，在现有分支与指定的远程分支之间
> git branch --set-upstream [branch] [remote-branch]
>
> # 合并指定分支到当前分支
> git merge [branch]
>
> # 选择一个commit，合并进当前分支
> git cherry-pick [commit]
>
> # 删除分支
> git branch -d [branch-name]
>
> # 删除远程分支
> git push origin --delete [branch-name]
> git branch -dr [remote/branch]
> ```

### 六、标签

> ```
> # 列出所有tag
> git tag
>
> # 新建一个tag在当前commit
> git tag [tag]
>
> # 新建一个tag在指定commit
> git tag [tag] [commit]
>
> # 删除本地tag
> git tag -d [tag]
>
> # 删除远程tag
> git push origin :refs/tags/[tagName]
>
> # 查看tag信息
> git show [tag]
>
> # 提交指定tag
> git push [remote] [tag]
>
> # 提交所有tag
> git push [remote] --tags
>
> # 新建一个分支，指向某个tag
> git checkout -b [branch] [tag]
> ```

### 七、查看信息

> ```
> # 显示有变更的文件
> git status
>
> # 显示当前分支的版本历史
> git log
>
> # 显示commit历史，以及每次commit发生变更的文件
> git log --stat
>
> # 搜索提交历史，根据关键词
> git log -S [keyword]
>
> # 显示某个commit之后的所有变动，每个commit占据一行
> git log [tag] HEAD --pretty=format:%s
>
> # 显示某个commit之后的所有变动，其"提交说明"必须符合搜索条件
> git log [tag] HEAD --grep feature
>
> # 显示某个文件的版本历史，包括文件改名
> git log --follow [file]
> git whatchanged [file]
>
> # 显示指定文件相关的每一次diff
> git log -p [file]
>
> # 显示过去5次提交
> git log -5 --pretty --oneline
>
> # 显示所有提交过的用户，按提交次数排序
> git shortlog -sn
>
> # 显示指定文件是什么人在什么时间修改过
> git blame [file]
>
> # 显示暂存区和工作区的差异
> git diff
>
> # 显示暂存区和上一个commit的差异
> git diff --cached [file]
>
> # 显示工作区与当前分支最新commit之间的差异
> git diff HEAD
>
> # 显示两次提交之间的差异
> git diff [first-branch]...[second-branch]
>
> # 显示今天你写了多少行代码
> git diff --shortstat "@{0 day ago}"
>
> # 显示某次提交的元数据和内容变化
> git show [commit]
>
> # 显示某次提交发生变化的文件
> git show --name-only [commit]
>
> # 显示某次提交时，某个文件的内容
> git show [commit]:[filename]
>
> # 显示当前分支的最近几次提交
> git reflog
> ```

### 八、远程同步

> ```
> # 下载远程仓库的所有变动
> git fetch [remote]
>
> # 显示所有远程仓库
> git remote -v
>
> # 显示某个远程仓库的信息
> git remote show [remote]
>
> # 增加一个新的远程仓库，并命名
> git remote add [shortname] [url]
>
> # 取回远程仓库的变化，并与本地分支合并
> git pull [remote] [branch]
>
> # 上传本地指定分支到远程仓库
> git push [remote] [branch]
>
> # 强行推送当前分支到远程仓库，即使有冲突
> git push [remote] --force
>
> # 推送所有分支到远程仓库
> git push [remote] --all
> ```

### 九、撤销

> ```
> # 恢复暂存区的指定文件到工作区
> git checkout [file]
>
> # 恢复某个commit的指定文件到暂存区和工作区
> git checkout [commit] [file]
>
> # 恢复暂存区的所有文件到工作区
> git checkout .
>
> # 重置暂存区的指定文件，与上一次commit保持一致，但工作区不变
> git reset [file]
>
> # 重置暂存区与工作区，与上一次commit保持一致
> git reset --hard
>
> # 重置当前分支的指针为指定commit，同时重置暂存区，但工作区不变
> git reset [commit]
>
> # 重置当前分支的HEAD为指定commit，同时重置暂存区和工作区，与指定commit一致
> git reset --hard [commit]
>
> # 重置当前HEAD为指定commit，但保持暂存区和工作区不变
> git reset --keep [commit]
>
> # 新建一个commit，用来撤销指定commit
> # 后者的所有变化都将被前者抵消，并且应用到当前分支
> git revert [commit]
>
> # 暂时将未提交的变化移除，稍后再移入
> git stash
> git stash pop
> ```

## 六. 其他Git命令

> ```
> # 生成一个可供发布的压缩包
> git archive
> ```

### 一、撤销提交

一种常见的场景是，提交代码以后，你突然意识到这个提交有问题，应该撤销掉，这时执行下面的命令就可以了。

> ```
> git revert HEAD
> ```

上面命令的原理是，在当前提交后面，新增一次提交，抵消掉上一次提交导致的所有变化。它不会改变过去的历史，所以是首选方式，没有任何丢失代码的风险。

`git revert` 命令只能抵消上一个提交，如果想抵消多个提交，必须在命令行依次指定这些提交。比如，抵消前两个提交，要像下面这样写。

> ```
> git revert [倒数第一个提交] [倒数第二个提交]
> ```

`git revert`命令还有两个参数。

> * `--no-edit`：执行时不打开默认编辑器，直接使用 Git 自动生成的提交信息。
> * `--no-commit`：只抵消暂存区和工作区的文件变化，不产生新的提交。

### 二、丢弃提交

如果希望以前的提交在历史中彻底消失，而不是被抵消掉，可以使用`git reset`命令，丢弃掉某个提交之后的所有提交。

> ```
> git reset [last good SHA]
> ```

`git reset`的原理是，让最新提交的指针回到以前某个时点，该时点之后的提交都从历史中消失。

默认情况下，`git reset`不改变工作区的文件（但会改变暂存区），`--hard`参数可以让工作区里面的文件也回到以前的状态。

> ```
> git reset --hard [last good SHA]
> ```

执行`git reset`命令之后，如果想找回那些丢弃掉的提交，可以使用`git reflog`命令，具体做法参考[这里](https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/#redo-after-undo-local)。不过，这种做法有时效性，时间长了可能找不回来。

### 三、替换上一次提交

提交以后，发现提交信息写错了，这时可以使用`git commit`命令的`--amend`参数，可以修改上一次的提交信息。

> ```
> git commit --amend -m "Fixes bug #42"
> ```

它的原理是产生一个新的提交对象，替换掉上一次提交产生的提交对象。

这时如果暂存区有发生变化的文件，会一起提交到仓库。所以，`--amend`不仅可以修改提交信息，还可以整个把上一次提交替换掉。

### 四、撤销工作区的文件修改

如果工作区的某个文件被改乱了，但还没有提交，可以用`git checkout`命令找回本次修改之前的文件。

> ```
> git checkout -- [filename]
> ```

它的原理是先找暂存区，如果该文件有暂存的版本，则恢复该版本，否则恢复上一次提交的版本。

注意，工作区的文件变化一旦被撤销，就无法找回了。

### 五、从暂存区撤销文件

如果不小心把一个文件添加到暂存区，可以用下面的命令撤销。

> ```
> git rm --cached [filename]
> ```

上面的命令不影响已经提交的内容。

### 六、撤销当前分支的变化

你在当前分支上做了几次提交，突然发现放错了分支，这几个提交本应该放到另一个分支。

> ```
> # 新建一个 feature 分支，指向当前最新的提交
> # 注意，这时依然停留在当前分支
> git branch feature
>
> # 切换到这几次提交之前的状态
> git reset --hard [当前分支此前的最后一次提交]
>
> # 切换到 feature 分支
> git checkout feature
> ```

上面的操作等于是撤销当前分支的变化，将这些变化放到一个新建的分支。
