资料援引
工作区和暂存区
- 工作区(Working Directory ) :在电脑里能看到的目录。
- 版本库(Repository) :工作区有一个隐藏目录
.git,这个不算工作区,而是Git的版本库。
- 暂存区:
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区。
撤销修改
还未 add
# 假设修改了 readme.txt 文件,但还没有 git add,此时通过 git status 命令来看看情况
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
# 廖雪峰老师的文章里git提示的撤销命令如下:
(use "git checkout -- <file>..." to discard changes in working directory)
# 但实践时我的git提示的撤销命令如下:
(use "git restore <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
# 现在丢弃工作区的修改
# 这里 <file> 之前的 -- 很重要,缺少的话命令就变成了“切换到另一个分支”
git checkout -- readme.txt
# restore 命令功能同上
git restore readme.txt
这里有两种情况:
- 一种是
<file>自修改后还没有被放到暂存区,撤销修改就回到和版本库相同的状态;
- 一种是
<file>已经添加到暂存区后,又作了修改,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。
已经 add 但没有 commit
# 假设修改了 readme.txt 文件,且执行了 git add,此时通过 git status 命令来看看情况
$ git status
On branch master
Changes to be committed:
# 廖雪峰老师的文章里git提示的撤销命令如下:
(use "git reset HEAD <file>..." to unstage)
# 但实践时我的git提示的撤销命令如下:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt
# 现在丢弃暂存区的修改
$ git reset HEAD readme.txt
Unstaged changes after reset:
M readme.txt
# restore 命令效果同上
git restore --staged readme.txt
git reset命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用HEAD时,表示最新的版本。
已经 commit 但没有 push
# 假设修改了 readme.txt 文件,且执行了 git add、git commit
# 此时通过 git status 命令来看看情况
$ git status
On branch <branch name>
nothing to commit, working tree clean
# 现在回退 commit 版本
# 1. 查看提交历史
git log
# 2. 从当前(HEAD)回退到某个之前的 commit_id
git reset --hard commit_id
# 3. 也可以查看命令历史,从而撤销第二步的回退
git reflog
# 4. 从 commit_id 回到 HEAD
git reset --hard HEAD
除--hard外,可选的参数还有--soft:
-
–-soft:回退到某个版本,只回退了commit的信息(只改变了HEAD指针的指向),不会清空暂存区(不会改变代码)。如果还要提交,直接commit即可; -
-–hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容,撤销的commit中所包含的更改被抹除掉,整个暂存区干净。
已经 push
# 1. 查看提交历史
git log
# 2. 从当前(HEAD)回退到某个之前的 commit_id,也可以用 --soft,自行选择。
git reset --hard commit_id
# 3. --force 强制提交当前版本
git push origin <branch name> -f
分支管理
切换到指定版本
git checkout <commit id>
git checkout本质上是用版本库里的版本替换工作区的版本,因此无论是修改还是删除工作区的内容,都可以“一键还原”。
创建并切换分支
git checkout -b <branch name>
# 相当于以下两条命令
git branch <branch name>
git checkout <branch name>
git checkout又是用来撤销修改,又是用来(创建)切换分支。同一个命令,有两种作用,有点令人迷惑。
实际上,切换分支这个动作,用switch更合理。
// 创建并切换分支
git switch -c <branch name>
// 切换到已有分支
git switch <branch name>
删除本地分支
git branch -d <branch name>
// 将 -d 改为 -D 可强制删除
查看当前分支
$ git branch
* dev // 当前分支前会有个 “*”
master
查看当前分支的父分支
// 严格来讲该命令是用来看 <branch name> 的操作记录的
// 只是可以通过在操作记录中寻找创建分支的命令,从而得知父分支是哪个
git reflog show <branch name>
合并/部分合并
git merge 合并
分支合并,将A分支合并到B分支。
// 切换到A分支
git checkout A
// 获取A分支最新代码
git pull
// 切换到B分支
git checkout B
// 获取B分支最新代码
git pull
// 合并分支
git merge A
合并有几种模式:
Fast-forward:快进模式,也就是直接把B指向A的当前提交,所以合并速度非常快。这种情况是因为从B上B1节点生成A分支后,只在A分支上有过提交,而直到将A合并到B,B仍处于B1节点,这等价于一直就在B分支上做修改,A1节点即是B1节点,An节点即是Bn节点。- 普通模式:B1节点生成A分支后,B和A都有新的操作,但两个分支的操作不涉及相同文件的修改,此时合并速度虽然不如快进模式快,但是不会引起冲突。
- 合并冲突:这是因为在两个分支上对同一个文件有了不同的操作导致的,通常分歧会以下面这种形式体现:
<<<<<<< HEAD
B 的操作
=======
A 的操作
>>>>>>> A
这种情况下需要手动修复冲突,再完成合并操作。
合并指定的文件
有时候想要合并A分支指定的文件或者文件夹到B分支上去,例如合并A分支的README.md文件到B分支上面。
// 切换到A分支
git checkout A
// 获取A分支最新代码
git pull
// 切换到B分支
git checkout B
// 获取B分支最新代码
git pull
// 合并指定文件或者文件夹到分支
git checkout A README.md
如果这里想要合并文件夹的话,比如src/views文件夹,可以将READMD.md换成文件夹src/views/**
撤销commit、add
如果合并错误,那么就先撤销commit,然后撤销add。
// 撤销commit
git reset --soft HEAD
// 撤销add
git reset HEAD
// 撤销指定的文件
git reset HEAD src/views/README.md
查看分支合并情况
git log --graph --pretty=oneline --abbrev-commit
# --graph 伪图形化查看分支合并图
# --pretty=oneline 单行省略显示(将不显示提交者和提交时间信息)
# --abbrev-commit 缩略显示版本ID
贮藏
贮藏当前操作
git stash
查看贮藏列表
$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log
应用贮藏
# 应用的同时删除缓存堆栈中的第一个stash
git stash pop
# 将缓存堆栈中的stash多次应用到工作目录中,但并不删除stash拷贝。
git stash apply
# 应用指定的stash
git stash apply stash@{0}
# 删除则通过
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)
# 或者删除所有缓存的stash
git stash clear
遴选
复制一个特定的提交到当前分支
git cherry-pick <commit id>
删除 untracked files(未监控)文件
// 删除 untracked files
git clean -f
// 连 untracked 的目录也一起删掉
git clean -fd
// 连 gitignore 的untrack 文件/目录也一起删掉 (慎用,一般这个是用来删掉编译出来的 .o之类的文件用的)
git clean -xfd
// 在用上述 git clean 前,强烈建议加上 -n 参数来先看看会删掉哪些文件,防止重要文件被误删
git clean -nxfd
git clean -nf
git clean -nfd
查看子模块版本
// 两种方式
git submodule
git submodule status
删除tag(标签)
删除本地标签
$ git tag -d v1.1.0
$ git tag --delete v1.1.0
# 列出所有以 `v2.31.5` 开头的本地标签(tags),然后通过 `xargs` 逐个删除这些本地的标签。
$ git tag -l 'v2.31.5*' | xargs git tag -d
检查本地标签是否被删除:
# 这个命令将会列出所有匹配本地标签。如果输出为空,则表示没有这样的本地标签了。
$ git tag -l 'v2.31.5*'
删除远程标签
$ git push --delete origin v1.1.0
# 如果你想删除所有以 `v2.31.5` 开头的远端标签,你可以使用以下命令:
# 这个命令将会列出所有匹配的本地标签,然后对每个标签执行一个 `git push origin --delete <tagname>` 的操作来删除远端的标签。
$ git tag -l 'v2.31.5*' | xargs -n 1 -I {} git push origin --delete {}
检查远端标签是否被删除:
# 这个命令将会显示所有匹配远端标签。在这里,`origin` 是远端的名字,如果你使用的是其他名称,需要相应地替换它。如果输出为空,说明远端标签也已经被删除了。
$ git ls-remote --tags origin 'v2.31.5*'
查看所有的提交,包括那些还没有合并到当前分支的提交
$ git log --graph --decorate --oneline --all