Git 常用命令
基本概念
以下基本概念相关的描述引自菜鸟教程:
- 工作区:就是你在电脑里能看到的目录
- 暂存区:英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)
- 版本库:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库
下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系:

- 图中左侧为工作区,右侧为版本库。在版本库中标记为 “index” 的区域是暂存区(stage/index),标记为 “master” 的是 master 分支所代表的目录树。
- 图中我们可以看出此时 “HEAD” 实际是指向 master 分支的一个”游标”。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。
- 图中的 objects 标识的区域为 Git 的对象库,实际位于 “.git/objects” 目录下,里面包含了创建的各种对象及内容。
- 当对工作区修改(或新增)的文件执行
git add命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。 - 当执行提交操作(
git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。 - 当执行
git reset HEAD命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。 - 当执行
git rm --cached <file>命令时,会直接从暂存区删除文件,工作区则不做出改变。 - 当执行
git checkout .或者git checkout -- <file>命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区中的改动。 - 当执行
git checkout HEAD .或者git checkout HEAD <file>命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。
为方便下面的描述,简单区分一下工作区、暂存区和版本库:
- 工作区:本地的项目中没有通过
git add添加到暂存区的文件 - 暂存区:本地的项目中通过
git add添加到暂存区的文件 - 版本库:本地的项目中通过
git commit添加到提交历史中的文件
基本命令
git init: 初始化一个本地git仓库repositorygit status: 查看本地仓库的状态addgit add <file>: 将工作区下的某个文件的修改加到暂存区(stage)git add .: 将工作区下的当前目录下的修改加到暂存区(stage)git add -A: 将工作区下的所有目录下的修改加到暂存区(stage)git add -u: 将工作区下的所有目录下的修改加到暂存区(stage),但是不包括新添加的文件
commitgit commit -m "<some message>": 将暂存区(stage)提交到版本库,并写一些描述信息git commit -m "<some message>" --no-verify: 将暂存区(stage)提交到版本库,并写一些描述信息,但是不会执行pre-commit钩子(慎用,通常在pre-commit钩子中做一些代码检查,如果不执行pre-commit钩子,那么就会跳过代码检查,这样就可能会导致一些问题)git commit: 将暂存区(stage)提交到版本库,会弹出一个编辑器,让你输入本次提交的说明,编辑器是git配置的默认编辑器,一般是vimgit commit --no-verify: 同上,只不过不会执行pre-commit钩子(慎用)
pushgit push: 将本地的最新改动记录,推送到远端的和本地分支同名的分支下(前提是远端有同名分支)git push -f: 强制推送,即使远端有比本地更新的提交记录,也会强制推送(慎用,可能会导致远端的提交记录丢失,执行这个之前务必知道自己在做什么)
clone 命令
git clone git@server-name:path/repo-name.git: 从远程仓库clone一个仓库到本地,同时会关联到远程仓库git clone -b <branchName> git@server-name:path/repo-name.git: 从远程clone指定分支到本地
merge & rebase 命令
mergegit merge <branchName>: 将指定branchName的分支合并到当前分支git merge --no-ff: 表示在系统采取fast-forward时让系统采用普通合并方式,而不是采用快速合并,普通合并会多一次commit,这样在以后查看时能够看到合并历史,而fast-forward合并则看不到曾经做过的合并
rebasegit rebase <branchName>: 将当前分支的改动,rebase到branchName分支上,这样就可以保证当前分支的提交记录是线性的,而不是分叉的,这样在以后查看提交记录时会更清晰。但是如果有冲突,可能需要多次解决冲突,比较麻烦git rebase -i: 交互式rebase,可以在rebase的过程中,对提交记录进行修改,比如说合并提交记录、删除提交记录等- Git rebase详细解析_GhostStories的博客-CSDN博客_git rebase 参数
fetch & pull 命令
git fetch: 从远程取回所有分支的更新(但不会自动合并)git fetch origin <branchName>: 从远程origin(origin换成其他名字,则从对应的远程仓库获取更新)取回branchName分支的更新(但不会自动合并)git pull: 从远程拉取最新的修改到本地并合并,如果有冲突则需要在本地解决。git pull相当于git fetch加上git merge
push 命令
git push -u origin master: 第一次将本地的repository的master分支推送到远程,以后再push就不用加-u了,关于-u的用法git push origin <branchName>: 将分支推送到远程仓库origin的branch分支上,如果远程仓库origin中没有叫做branchName的分支,则会创建一个
如果 git push 省略了远程仓库名则默认会将本地记录推送到名为 origin 的远程仓库上,且目标推送分支是 origin 远程仓库中和本地仓库分支同名的分支
stash 命令
git stash: 把当前工作现场先存起来(不是add也不是commit),可以暂时去做其他事,之后(主要应用场景是当前手头工作没做完不能commit,但是又有紧急的bug需要切换到其他分支上去修复)git stash list: 查看所有存起来的工作现场git stash pop: 将stash list中,最顶部的工作现场恢复,并将list顶部的给删除掉git stash apply <指定stash>: 将指定的工作现场恢复,但是在stash list当中并不删除指定stashgit stash clear: 情况stash list(慎用)
checkout 命令
git checkout -- <file>: 将工作区中file文件的修改撤销,包括把删除的file复原(在添加到暂存区之前才有效,如果将工作区文件的修改add到了暂存区,那么再使用这个命令就会无效,这时应该先把暂存区的修改撤销,再使用这个命令)git checkout <branchName>: 切换到branchName分支上git checkout -b <branchName>: 是上面git branch <branchName>和git checkout <branchName>两条命令合并之后的简写,即创建一个名为name的分支并切换到那个分支git checkout <commit_id>: 切换到指定的commit(可以是当前分支也可以是其他分支)
reset 命令
git reset --hard HEAD^/git reset --hard HEAD~1: 将git的HEAD指针指向版本库中上一个版本,同时也会把整个项目的文件回退到上一个版本。这个会把最近的一个commit以及对应的改动完全丢弃,不再保留。如需要多回退几个版本,可以使用HEAD~n,n表示回退几个版本git reset --hard <commit_id>: 将git的HEAD指针指向版本库中某个commit指定的版本,同时也会把整个项目的文件回退到这个版本。这个会把commit_id之后的所有commit以及对应的改动完全丢弃,不再保留git reset --soft HEAD^/git reset --soft HEAD~1: 将将git的HEAD指针指向版本库中上一个版本,但是不会修项目的文件,会把上一个版本的改动,保留到git暂存区(不会修改工作区,只改暂存区)。如果当前暂存区有内容,会将上一个版本的改动和暂存区的内容合并到暂存区git reset --soft <commit_id>: 将将git的HEAD指针指向版本库中某个commit指定的版本,但是不会修项目的文件,会把commit_id之后的所有commit对应的改动,保留到git暂存区(不会修改工作区,只改暂存区)。如果当前暂存区有内容,会将commit_id之后的所有commit对应的改动和暂存区的内容合并到暂存区git reset --mixed HEAD^/git reset --mixed HEAD~1/git reset HEAD^/git reset HEAD~1: 将git的HEAD指针指向版本库中上一个版本,但是不会修项目的文件,会把上一个版本的改动,保留到git工作区,同时也会将暂存区的内容清空。如果当前暂存区有内容,会将上一个版本的改动和暂存区的内容合并到工作区git reset --mixed <commit_id>/git reset <commit_id>: 将git的HEAD指针指向版本库中某个commit指定的版本,但是不会修项目的文件,会把commit_id之后的所有commit对应的改动,保留到git工作区,同时也会将暂存区的内容清空。如果当前暂存区有内容,会将commit_id之后的所有commit对应的改动和暂存区的内容合并到工作区
其中,--hard、--soft、--mixed是三种不同的模式,--mixed是默认的模式,如果不指定模式,那么就是--mixed模式。
可以理解为 git reset --hard 通常用于丢弃最近的提交,而另外两个通常用于修改最近的提交。
revert 命令
git revert <commit_id>: 撤销提交记录中间的某次commit,同时会生成一条新的commit记录git revert -m <commit_id>: 撤销提交记录中间的某次commit,同时会生成一条新的commit记录(这次commit是别的分支合并过来的)git revert --no-commit commit_id1..commit_id2: 撤销提交记录中间的几个连续的commit(注意这是一个左开右闭区间,即不包括commit_id1,但包括commit_id2)
cherry-pick 命令
git cherry-pick <commit_id>: 将另一个分支上的指定提交记录,摘抄到当前分支git cherry-pick <commit_id1>..<commit_id2>: 将另一个分支上的从commit_id1到commit_id2之间的提交记录摘抄到到当前分支
详见:git 场景——从一个分支cherry-pick一个或多个commit到另一个分支
branch 命令
git branch: 查看本地所有分支,当前所在分支前面有一个*号git branch -r: 查看远程所有分支git branch -a: 查看本地和远程所有分支git branch -d <name>: 删除名为name的分支git branch -D <name>: 强行删除名为name的分支,用-d删除的话,如果name分支没有被合并就无法删除,那么就要用-Dgit branch <name>: 创建一个名为name的分支
log 命令
git log: 查看提交的日志信息(按q退出)git log --pretty=oneline: 查看只有一行的简略日志信息git log --graph: 查看日志信息的图示信息git log --graph --pretty=oneline --abbrev-commit: 查看日志信息的图示信息,且只有一行的简略日志信息,且commit id只显示前几位
remote 命令
git remote: 查看远程仓库信息,远程库默认名字是origingit remote -v: 查看更详细的远程仓库信息git remote add origin git@server-name:path/repo-name.git: 关联一个远程库,或者说添加一个远程库(一个本地仓库可以有多个远程库),在这里添加的远程仓库名为origingit remote remove origin: 删除已添加的远程仓库地址,这里删除的是名为origin的远程仓库git remote set-url origin https://xxxxx.xxx/repo-name.git: 更改远程仓库origin的url,比如说github项目地址改了,或者运维配置的gitlab域名变更了,可以用这个修改git remote rename origin old-origin: 重命名远程仓库,将远程仓库origin重命名为old-origin
diff 命令
git diff <filename>: 不带参数就是比较工作区和暂存区中filename文件的不同git diff --cached <filename>: 比较暂存区和版本库中filename文件的不同git diff HEAD <filename>: 比较工作区和版本库中filename文件的不同
rev-parse 命令
git rev-parse HEAD: 获取当前分支的commit idgit rev-parse --short HEAD: 获取当前分支的commit id的前几位git rev-parse --abbrev-ref HEAD: 获取当前分支名git rev-parse --symbolic --branches: 获取所有分支的名字git rev-parse --show-toplevel: 获取当前git仓库的根目录的绝对路径git rev-parse --git-dir: 获取当前git仓库的.git目录git rev-parse --show-prefix: 获取当前目录相对于git仓库根目录的相对路径git rev-parse branchName: 获取本地分支branchName的commit idgit rev-parse origin/branchName: 获取远程分支branchName的commit idgit rev-parse origin/branchName^{commit}: 和上面一样,获取远程分支branchName的commit id
其他
1. 首次关联本地仓库到远端空仓库
git remote add origin git@server-name:path/repo-name.git: 关联一个远程库git push -u origin master: 第一次将本地的repository的master分支推送到远程,以后再push就不用加-u了
2. push时其他人已经push了新的提交,被reject
如果多个人同时关联了远程库(多人协作时),在push的时候,发现有人已经向remote推送了他的提交,而你对同样的文件做了修改,那么在push的时候就会报错rejected推送失败,因为有冲突,所以这时候应该git pull拉取最新的提交并(自动地)在本地合并,产生冲突,然后本地解决冲突之后再add、commit,再push
3. 在本地新建一个分支再push到远端
git checkout -b newBranchgit push origin newBranch
4. 对于已经添加到的.gitignore文件,仍然会被trace到的解决方法
git rm --cached dir_name/file_name
原因是添加了 .gitignore 忽略这些路径后, 由于这个路径是已经增加到过仓库管理中,所以尽管已经在 ignore 列表里,依然 会被 git trace 到每个文件的变化。这时只需删除已缓存的trace就可以了