534 lines
16 KiB
Markdown
534 lines
16 KiB
Markdown
---
|
||
title: Git基础与命令
|
||
date: 2020-11-18 17:43:57
|
||
permalink: /pages/635088/
|
||
categories:
|
||
- 《Git》学习笔记
|
||
tags:
|
||
- Git
|
||
author:
|
||
name: xugaoyi
|
||
link: https://github.com/xugaoyi
|
||
---
|
||
# Git基础与命令
|
||
|
||
官方文档(中文):https://git-scm.com/book/zh/v2
|
||
|
||
> 本文档是根据官方文档来编写的,以官方文档为准。
|
||
|
||
|
||
|
||
## Git基础
|
||
|
||
### 全局配置
|
||
|
||
```bash
|
||
git config --global user.name 'your name'
|
||
git config --global user.email 'xxx@xx.com'
|
||
```
|
||
|
||
自报家门
|
||
|
||
|
||
|
||
### 检查配置信息
|
||
|
||
```sh
|
||
git config --list
|
||
```
|
||
|
||
|
||
|
||
### 获取帮助
|
||
|
||
```sh
|
||
# 获取全局帮助手册
|
||
git help
|
||
|
||
# 获取特定命令的详细版帮助手册 (两个命令是等价的)
|
||
git help <某个命令>
|
||
git <某个命令> --help # 两个横杠
|
||
|
||
# 获取特定命令的简明版帮助手册
|
||
git <某个命令> -h # 一个横杠
|
||
```
|
||
|
||
|
||
|
||
### 初始化仓库
|
||
|
||
```sh
|
||
# 本地目录初始化仓库
|
||
git init
|
||
```
|
||
|
||
如果你是从远程仓库clone的项目,则该项目是已经初始化好的git仓库
|
||
|
||
|
||
|
||
### 克隆远程仓库
|
||
|
||
```sh
|
||
# 克隆
|
||
git clone <url>
|
||
|
||
# 克隆同时修改目录名
|
||
git clone <url> <name>
|
||
```
|
||
|
||
初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态,因为 Git 刚刚检出了它们, 而你尚未编辑过它们
|
||
|
||
|
||
|
||
### 检查文件状态
|
||
|
||
```sh
|
||
# 查看详细状态说明
|
||
git status
|
||
|
||
# 查看简明状态说明
|
||
git status -s # -s 或 --short
|
||
M README # 已修改,但未暂存 (M的位置靠右,红色)
|
||
MM Rakefile # 已修改,暂存后又作了修改(有暂存和未暂存)
|
||
A lib/git.rb # 新添加到暂存区,未提交
|
||
M lib/simplegit.rb # 已修改,已暂存 (M的位置靠左,绿色)
|
||
?? LICENSE.txt # 新添加,未跟踪
|
||
```
|
||
|
||
* git目录中的文件状态包含:是否跟踪、是否修改、是否已存入暂存区
|
||
|
||
* 参数的**一个横杠表示缩写,两个横杠表示全称。**
|
||
|
||
|
||
|
||
### 加入暂存区 (跟踪文件)
|
||
|
||
```sh
|
||
# 文件加入暂存区(跟踪指定文件)
|
||
git add <files>
|
||
```
|
||
|
||
`git add` 命令使用文件或目录的路径作为参数;如果参数是目录的路径,该命令将递归地跟踪该目录下的所有文件。
|
||
|
||
`add` 命令是将文件**加入**到**暂存区**,`commit` 命令的**提交**到**本地仓库**,`push` 命令是**推送**到**远程仓库**。
|
||
|
||
|
||
|
||
### 忽略文件
|
||
|
||
添加一个名为 `.gitignore` 的文件,列出要忽略的文件的模式
|
||
|
||
```sh
|
||
*.[oa] # 忽略以 .o 或 .a 结尾的文件(一般这类文件是编译过程出现)
|
||
*~ # 忽略以 ~ 结尾的文件(一般是文本编辑软件保存的副本)
|
||
```
|
||
|
||
文件 `.gitignore` 的**格式规范**如下:
|
||
|
||
- 所有空行或者以 `#` 开头的行都会被 Git 忽略(注释符号)。
|
||
|
||
- 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
|
||
|
||
> glob 模式是指 shell 所使用的简化了的正则表达式
|
||
|
||
- 匹配模式可以以(`/`)开头防止递归。
|
||
|
||
- 匹配模式可以以(`/`)结尾指定目录。
|
||
|
||
- 要忽略指定模式以外的文件或目录,可以在模式前加上叹号(`!`)取反。
|
||
|
||
---
|
||
|
||
* 星号(`*`)匹配零个或多个任意字符
|
||
* `[abc]` 匹配任何一个列在方括号中的字符 (这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c)
|
||
* 问号(`?`)只匹配一个任意字符
|
||
* `[0-9]` 表示匹配所有 0 到 9 的数字。在方括号中使用短划线分隔两个字符, 表示所有在这两个字符范围内的都可以匹配
|
||
* 使用两个星号(`**`)表示匹配任意中间目录,比如 `a/**/z` 可以匹配 `a/z` 、 `a/b/z` 或 `a/b/c/z` 等。
|
||
|
||
```sh
|
||
# 忽略所有的 .a 文件
|
||
*.a
|
||
|
||
# 但跟踪所有的 lib.a,即便你在前面忽略了 .a 文件
|
||
!lib.a
|
||
|
||
# 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO
|
||
/TODO
|
||
|
||
# 忽略任何目录下名为 build 的文件夹
|
||
build/
|
||
|
||
# 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt
|
||
doc/*.txt
|
||
|
||
# 忽略 doc/ 目录及其所有子目录下的 .pdf 文件
|
||
doc/**/*.pdf
|
||
```
|
||
|
||
GitHub 有一个十分详细的针对数十种项目及语言的 `.gitignore` 文件列表, 你可以在 https://github.com/github/gitignore 找到它。
|
||
|
||
|
||
|
||
### 查看修改的具体内容
|
||
|
||
```sh
|
||
git diff # 比较修改之后还没有暂存起来的变化内容。
|
||
|
||
git diff --staged # 查看已暂存的将要添加到下次提交里的内容
|
||
```
|
||
|
||
`git status` 只能查看文件变动的状态,并不能查看具体修改了哪些内容。使用`git diff`可以看到具体变动的内容。
|
||
|
||
|
||
|
||
### 提交更新
|
||
|
||
```sh
|
||
git commit # 未带参数的会打开默认文本编辑器让你输入提交说明
|
||
|
||
git commit -m '提交说明' # 带-m参数直接输入提交说明
|
||
```
|
||
|
||
使用`git commit`提交更新,在此之前,务必确认所有变动已经被`git add`添加到暂存区。
|
||
|
||
|
||
|
||
### 跳过使用暂存区域
|
||
|
||
```sh
|
||
git commit -a -m '提交说明'
|
||
```
|
||
|
||
添加`-a`选项可以跳过`git add` 步骤,把已经跟踪过的文件一并提交。
|
||
|
||
注意:这个操作**无法提交未跟踪**的文件。
|
||
|
||
|
||
|
||
## Git 基础 - 查看提交历史
|
||
|
||
```sh
|
||
git log
|
||
```
|
||
|
||
不传入任何参数的默认情况下,`git log` 会按时间先后顺序列出所有的提交,最近的更新排在最上面。
|
||
|
||
此命令打印的数据中有一项是一长串的 `SHA-1` 校验码。
|
||
|
||
**带入`-p`或`--patch `查看提交的具体差异:**
|
||
|
||
```sh
|
||
git log -p -2 # -p显示差异 -2显示最近的提交次数
|
||
```
|
||
|
||
`--stat` 显示每次提交的差异统计
|
||
|
||
```sh
|
||
git log --stat
|
||
```
|
||
|
||
`--pretty` 这个选项可以使用不同于默认格式的方式展示提交历史
|
||
|
||
这个选项有一些内建的子选项供你使用。 比如 `oneline` 会将每个提交放在一行显示,在浏览大量的提交时非常有用。 另外还有 `short`,`full` 和 `fuller` 选项,它们展示信息的格式基本一致,但是详尽程度不一:
|
||
|
||
```fsharp
|
||
$ git log --pretty=oneline
|
||
ca82a6dff817ec66f44342007202690a93763949 changed the version number
|
||
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test
|
||
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit
|
||
```
|
||
|
||
最有意思的是 `format` ,可以定制记录的显示格式。 这样的输出对后期提取分析格外有用——因为你知道输出的格式不会随着 Git 的更新而发生改变:
|
||
|
||
```sh
|
||
$ git log --pretty=format:"%h - %an, %ar : %s"
|
||
ca82a6d - Scott Chacon, 6 years ago : changed the version number
|
||
085bb3b - Scott Chacon, 6 years ago : removed unnecessary test
|
||
a11bef0 - Scott Chacon, 6 years ago : first commit
|
||
```
|
||
|
||
[`git log --pretty=format` 常用的选项](https://git-scm.com/book/zh/v2/ch00/pretty_format) 列出了 `format` 接受的常用格式占位符的写法及其代表的意义。
|
||
|
||
**当 `oneline` 或 `format` 与另一个 `log` 选项 `--graph` 结合使用时尤其有用。 这个选项添加了一些 ASCII 字符串来形象地展示你的分支、合并历史:**
|
||
|
||
``` sh
|
||
$ git log --pretty=format:"%h %s" --graph
|
||
* 2d3acf9 ignore errors from SIGCHLD on trap
|
||
* 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|
||
|\
|
||
| * 420eac9 Added a method for getting the current branch.
|
||
* | 30e367c timeout code and tests
|
||
* | 5a09431 add timeout protection to grit
|
||
* | e1193f8 support for heads with slashes in them
|
||
|/
|
||
* d6016bc require time for xmlschema
|
||
* 11d191e Merge branch 'defunkt' into local
|
||
```
|
||
|
||
|
||
|
||
## Git 基础 - 撤消操作
|
||
|
||
你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
|
||
|
||
```sh
|
||
$ git commit -m 'initial commit'
|
||
$ git add forgotten_file
|
||
$ git commit --amend # 重新提交,且只有一次提交记录
|
||
```
|
||
|
||
最终你只会有一个提交——第二次提交将代替第一次提交的结果。
|
||
|
||
更多撤销操作请了解 `reset`命令。
|
||
|
||
|
||
|
||
## Git 基础 - 远程仓库的使用
|
||
|
||
### 查看远程仓库
|
||
|
||
```sh
|
||
git remote # 仅显示远程仓库的名称
|
||
git remote -v # 显示远程仓库的名称 + 地址
|
||
```
|
||
|
||
### 添加远程仓库
|
||
|
||
```sh
|
||
git remote add <远程仓库名> <url>
|
||
```
|
||
|
||
### 从远程仓库中抓取与拉取
|
||
|
||
就如刚才所见,从远程仓库中获得数据,可以执行:
|
||
|
||
```sh
|
||
git fetch <remote>
|
||
```
|
||
|
||
这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
|
||
|
||
注意: `git fetch` 命令只会将数据下载到你的本地仓库——**它并不会自动合并或修改你当前的工作**。 当准备好时你必须手动将其合并入你的工作。
|
||
|
||
```sh
|
||
git pull
|
||
```
|
||
|
||
用 `git pull` 命令来自动抓取后合并该远程分支到当前分支。 这或许是个更加简单舒服的工作流程。默认情况下,`git clone` 命令会自动设置本地 master 分支跟踪克隆的远程仓库的 `master` 分支(或其它名字的默认分支)。 **运行 `git pull` 通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。**
|
||
|
||
|
||
|
||
### 推送到远程仓库
|
||
|
||
```sh
|
||
git push <remote> <branch> # git push origin master
|
||
```
|
||
|
||
### 查看某个远程仓库
|
||
|
||
```sh
|
||
git remote show <remote> # git remote show origin
|
||
```
|
||
|
||
查看远程仓库的详细信息。这个命令列出了当你在特定的分支上执行 `git push` 会自动地推送到哪一个远程分支
|
||
|
||
### 远程仓库的重命名与移除
|
||
|
||
```sh
|
||
git remote rename <原名> <新名> # 重命名
|
||
git remote remove paul <remote># 移除远程仓库
|
||
```
|
||
|
||
|
||
|
||
## Git 基础 - 打标签
|
||
|
||
### 列出标签
|
||
|
||
```sh
|
||
git tag # 完整标签列表
|
||
git tag -l "v2.0*" # 只显示包含 v2.0 的标签。 注意加星号(*)
|
||
```
|
||
|
||
`-l` 或 `--list` 都可以。
|
||
|
||
### 创建标签
|
||
|
||
Git 支持两种标签:**轻量标签**(lightweight)与**附注标签**(annotated)。
|
||
|
||
轻量标签很像一个不会改变的分支——它**只是某个特定提交的引用**。
|
||
|
||
而附注标签是存储在 Git 数据库中的一个完整对象, 它们是可以被校验的,其中**包含打标签者的名字、电子邮件地址、日期时间, 此外还有一个标签信息**,并且可以使用 GNU Privacy Guard (GPG)签名并验证。 通常会建议创建附注标签,这样你可以拥有以上所有信息。但是如果你只是想用一个临时的标签, 或者因为某些原因不想要保存这些信息,那么也可以用轻量标签。
|
||
|
||
### 附注标签
|
||
|
||
```sh
|
||
git tag -a v1.4 -m "my version 1.4" # -a表示add, -m 表示附件信息
|
||
```
|
||
|
||
通过使用 `git show` 命令可以看到标签信息和与之对应的提交信息:
|
||
|
||
```sh
|
||
git show v1.4
|
||
```
|
||
|
||
### 轻量标签
|
||
|
||
轻量标签本质上是将提交校验和存储到一个文件中——没有保存任何其他信息。 创建轻量标签,不需要使用 `-a`、`-s` 或 `-m` 选项,只需要提供标签名字:
|
||
|
||
```sh
|
||
git tag v1.4-lw # 不需要添加选项
|
||
```
|
||
|
||
这时,如果在标签上运行 `git show`,你不会看到额外的标签信息。 命令只会显示出提交信息:
|
||
|
||
```sh
|
||
$ git show v1.4-lw
|
||
commit ca82a6dff817ec66f44342007202690a93763949
|
||
Author: Scott Chacon <schacon@gee-mail.com>
|
||
Date: Mon Mar 17 21:52:11 2008 -0700
|
||
```
|
||
|
||
### 后期打标签
|
||
|
||
你也可以对过去的提交打标签。 假设提交历史是这样的:
|
||
|
||
```sh
|
||
$ git log --pretty=oneline
|
||
166ae0c4d3f420721acbb115cc33848dfcc2121a started write support
|
||
9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile
|
||
8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme
|
||
```
|
||
|
||
现在,假设在 v1.2 时你忘记给项目打标签,也就是在 “updated rakefile” 提交。 你可以在之后补上标签。 要在那个提交上打标签,你需要在命令的末尾指定提交的校验和(或部分校验和):
|
||
|
||
```sh
|
||
$ git tag -a v1.2 9fceb02 # 打的标签属于附注标签
|
||
```
|
||
|
||
### 共享标签
|
||
|
||
`git push` 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 这个过程就像共享远程分支一样——你可以运行 `git push origin <tagname>`。
|
||
|
||
```sh
|
||
git push origin v1.5 # 显式地推送标签到远程仓库
|
||
git push origin --tags # 一次性推送所有不在远程仓库上的标签
|
||
```
|
||
|
||
现在,当其他人从仓库中克隆或拉取,他们也能得到你的那些标签。
|
||
|
||
### 删除标签
|
||
|
||
要删除掉你本地仓库上的标签,可以使用命令 `git tag -d <tagname>`。 例如,可以使用以下命令删除一个轻量标签:
|
||
|
||
```sh
|
||
$ git tag -d v1.4-lw
|
||
Deleted tag 'v1.4-lw' (was e7d5add)
|
||
```
|
||
|
||
注意上述命令并不会从任何远程仓库中移除这个标签,你必须用 `git push <remote> :refs/tags/<tagname>` 来更新你的远程仓库:
|
||
|
||
第一种变体是 `git push <remote> :refs/tags/<tagname>` :
|
||
|
||
```sh
|
||
$ git push origin :refs/tags/v1.4-lw
|
||
To /git@github.com:schacon/simplegit.git
|
||
- [deleted] v1.4-lw
|
||
```
|
||
|
||
上面这种操作的含义是,将冒号前面的空值推送到远程标签名,从而高效地删除它。
|
||
|
||
第二种更直观的删除远程标签的方式是:
|
||
|
||
```sh
|
||
$ git push origin --delete <tagname>
|
||
```
|
||
|
||
|
||
|
||
### 检出标签
|
||
|
||
如果你想查看某个标签所指向的文件版本,可以使用 `git checkout` 命令, 虽然这会使你的仓库处于“分离头指针(detached HEAD)”的状态——这个状态有些不好的副作用:
|
||
|
||
```sh
|
||
$ git checkout 2.0.0
|
||
Note: checking out '2.0.0'.
|
||
|
||
You are in 'detached HEAD' state. You can look around, make experimental
|
||
changes and commit them, and you can discard any commits you make in this
|
||
state without impacting any branches by performing another checkout.
|
||
|
||
If you want to create a new branch to retain commits you create, you may
|
||
do so (now or later) by using -b with the checkout command again. Example:
|
||
|
||
git checkout -b <new-branch>
|
||
|
||
HEAD is now at 99ada87... Merge pull request #89 from schacon/appendix-final
|
||
|
||
$ git checkout 2.0-beta-0.1
|
||
Previous HEAD position was 99ada87... Merge pull request #89 from schacon/appendix-final
|
||
HEAD is now at df3f601... add atlas.json and cover image
|
||
```
|
||
|
||
在“分离头指针”状态下,如果你做了某些更改然后提交它们,标签不会发生变化, 但你的新提交将不属于任何分支,并且将无法访问,除非通过确切的提交哈希才能访问。 因此,如果你需要进行更改,比如你要修复旧版本中的错误,那么通常需要创建一个新分支:
|
||
|
||
```sh
|
||
$ git checkout -b version2 v2.0.0
|
||
Switched to a new branch 'version2'
|
||
```
|
||
|
||
如果在这之后又进行了一次提交,`version2` 分支就会因为这个改动向前移动, 此时它就会和 `v2.0.0` 标签稍微有些不同,这时就要当心了。
|
||
|
||
|
||
|
||
## Git 命令别名
|
||
|
||
Git 并不会在你输入部分命令时自动推断出你想要的命令。 如果不想每次都输入完整的 Git 命令,可以通过 `git config` 文件来轻松地为每一个命令设置一个别名。 这里有一些例子你可以试试:
|
||
|
||
```sh
|
||
$ git config --global alias.co checkout
|
||
$ git config --global alias.br branch
|
||
$ git config --global alias.ci commit
|
||
$ git config --global alias.st status
|
||
```
|
||
|
||
这意味着,当要输入 `git commit` 时,只需要输入 `git ci`。
|
||
|
||
在创建你认为应该存在的命令时这个技术会很有用。 例如,为了解决取消暂存文件的易用性问题,可以向 Git 中添加你自己的取消暂存别名:
|
||
|
||
```sh
|
||
$ git config --global alias.unstage 'reset HEAD --'
|
||
```
|
||
|
||
这会使下面的两个命令等价:
|
||
|
||
```sh
|
||
$ git unstage fileA
|
||
$ git reset HEAD -- fileA
|
||
```
|
||
|
||
这样看起来更清楚一些。 通常也会添加一个 `last` 命令,像这样:
|
||
|
||
```sh
|
||
$ git config --global alias.last 'log -1 HEAD'
|
||
```
|
||
|
||
这样,可以轻松地看到最后一次提交:
|
||
|
||
```sh
|
||
$ git last
|
||
commit 66938dae3329c7aebe598c2246a8e6af90d04646
|
||
Author: Josh Goebel <dreamer3@example.com>
|
||
Date: Tue Aug 26 19:48:51 2008 +0800
|
||
|
||
test for current head
|
||
|
||
Signed-off-by: Scott Chacon <schacon@example.com>
|
||
```
|
||
|
||
可以看出,Git 只是简单地将别名替换为对应的命令。 然而,你可能想要执行外部命令,而不是一个 Git 子命令。 如果是那样的话,可以在命令前面加入 `!` 符号。 如果你自己要写一些与 Git 仓库协作的工具的话,那会很有用。 我们现在演示将 `git visual` 定义为 `gitk` 的别名:
|
||
|
||
```sh
|
||
$ git config --global alias.visual '!gitk'
|
||
```
|