本文用来整顿记录日常工作中常常应用到的 Git 命令,不便日常查问应用。对于 Git 诞生的历史及相干外部原理本文暂不波及。
根底概念
首先通过下图 (图片来源于网络) 理解日常工作中 Git 的操作流程
基于上图介绍下 Git 的相干工作空间
术语 | 解释 |
---|---|
本地仓库 / 版本库(Repository) | 一个仓库包含了所有的版本信息、所有的分支和 Tag 信息。在 Git 中仓库的每份拷贝都是残缺的。仓库让你能够从中获得你的工作正本。工作区目录下有一个 .git 的目录,这个目录就是 Git 来跟踪治理版本库的。 |
工作区(workspace) | 本地我的项目寄存源文件的文件夹就是工作区 |
暂存区(Index/Stage) | 用于长期跟踪存储相干批改 |
近程仓库(Remote) | 个别指托管在近程 Git 服务器上的仓库,用于托管文件供多人合作应用。如 GitHub、Gitee、自建的 Git 服务等 |
罕用操作
根底配置
-
配置提交人标识和邮箱
$ git config --global user.name 'your_name' $ git config --global user.email 'your_email@domain.com' # 勾销配置 git config --unset --global user.name git config --unset --global user.email
config 的三个作用域
$ git config --local #local 只对某个仓库无效 $ git config --global #global 对以后用户所有仓库无效 $ git config --system # system 对系统所有登录的用户无效 # local 的优先级最高,即 local 的配置信息能够笼罩 global 配置的信息
显示 config 的配置
$ git config --list --local $ git config --list --global $ git config --list --system
删除 config 中的配置项
git config --global --remove-section name # name 为要删除的配置项名称
执行
git config
后相干的配置将保留到纯文本文件中,对应生成文件地位如下:- local 在.git/config 外面;
- global 在集体 home 目录下的 .gitconfig 外面;
- system 在 git 装置目录的下。
所以 git config
命令其实只是一个提供便捷命令行的接口。
-
其余罕用配置
$ git config --global alias.lg "log -10 --pretty=oneline --graph" # 别名设置 $ git config --system core.editor <editor> $ git config --global --edit
仓库初始化
-
本地有个我的项目代码写了一段时间了,但还没有用 git 治理起来,当初想用 git 做版本治理
$ cd existing_floder $ git init $ git add . $ git commit -m"Initial commit"
-
本地生成了一个 git 的仓库,开发了一段时间,想把这个 git 仓库提交到公司 git 服务器新建的我的项目中
$ cd existiong_repo $ git remote add origin git@your_git_server:your_group/your_project.git $ git push -u origin master #如想推送所有分支和 tag 则按如下执行 $ git push -u origin --all $ git push -u origin --tags # 官网解释:push 后减少 -u 的目标是对于每个最新的或胜利 push 的分支,增加 upstream(跟踪)援用。该援用由后续供无参数 git-pull 和其余命令应用。# 文言解释:也就是加上了 - u 参数,Git 岂但会把本地的 master 分支内容推送的近程新的 master 分支,还会把本地的 master 分支和近程的 master 分支关联起来,在当前的推送或者拉取时就能够简化命令。
-
仓库曾经在公司 git 服务器上创立,检出仓库到本地
$ git clone git@your_git_server:your_group/your_project.git #克隆指定分支 $ git clone -b master git@your_git_server:your_group/your_project.git
Git 反对多种协定,默认的
git://
应用 ssh,但也能够应用https
等其余协定。应用
https
除了速度慢以外,还有个最大的麻烦是每次推送都必须输出口令,然而在某些只凋谢 http 端口的公司外部就无奈应用ssh
协定而只能用https
。同时作为服务端存储的 Git 仓库都为裸仓库,裸仓库没有工作区,因为服务器上的 Git 仓库纯正是为了共享,所以不让用户间接登录到服务器下来改工作区,并且服务器上的 Git 仓库通常都以
.git
结尾# 在 Github、GitLab 等 Git 托管服务上创立的仓库均为裸仓库。如想手动创立可参考如下命令 #以下命令都能够生成裸仓库 $ git clone --bare ... $ git init --bare
本地仓库变更
Git 的版本库里存了很多货色,其中最重要的就是称为 stage(或者叫 index)的暂存区,还有 Git 为咱们主动创立的第一个分支 master
,以及指向master
的一个指针叫HEAD
。
git status
查看工作目录和暂存区的状态
git add
git add .
将工作区新增、批改和删除 (Git 2.x 才反对) 的文件增加到暂存区,即对 Git 已跟踪和未跟踪的文件都进行解决。只对以后门路及其子门路文件无效git add -A
等同于git add --all
将文件的批改、新增、删除增加到暂存区。对整个仓库下的文件都无效git add -u
将工作区内被批改和删除的文件增加到暂存区(不包含未被 Git 跟踪的新增文件)。益处是能够防止把工作区内还没筹备好的文件加到暂存区git add file1 file2 ...
一次可增加一个或多个文件及文件夹到暂存区git add -p file
交互式的抉择工作区及暂存区文件的相干补丁,并新增到暂存区git commit
git commit -m'xxx'
提交暂存区的变更内容到版本历史, 生成 commit 节点git commit -am'xxx'
会将曾经被 Git 跟踪的变更内容,提交到暂存区并提交内容, 生成 commit 节点。== 不举荐此形式 ==,此形式将工作的文件间接增加到版本历史中去了,跳过了人工进行暂存的解决。
疏忽指定文件
能够配置 Git 疏忽指定的文件或者是文件夹。这些配置都放在 .gitignore
文件中。这个文件能够存在于不同的文件夹中,能够蕴含不同的文件匹配模式。
具体配置在此不做具体形容,上面介绍下应用中可能遇见的问题:
-
某文件已被提交到近程仓库后,想对其疏忽
git rm --cached <file>
- 在
.gitignore
文件中减少配置 - 从新提交推送至近程仓库即可
-
空文件夹无奈被追踪
因为 Git 是针对文件的变更做治理追踪的故须要在文件夹下增加文件
.gitkeep
, 这是一个约定俗成的文件名。递归创立.gitkeep 文件
find . -type d -empty -not -path"./.git/*" -exec touch {}/.gitkeep \\;
文件重命名
- 惯例形式
mv readme readme.md
执行
git add readme.md
和git rm readme
- 简便形式
git mv readme readme.md
, 前提此文件已被 Git 所治理跟踪间接执行 commit 即可,有效执行 add。
删除文件
-
惯例形式
$ rm <file> # 删除工作区文件 $ git rm <file> # 退出暂存区
-
简便形式
$ git rm <file> # 删除文件并退出暂存区
差别比照
-
比拟暂存区与 HEAD 所指向分支所含文件的差别
$ git diff --cached $ git diff --staged # 以上命令皆可
-
比拟工作区与暂存区所含文件的差别
$ git diff #默认比拟的是工作区与暂存区所有文件的差别 $ git diff -- file1 file2 #能够指定一个或多个具体文件名,在工作区与暂存区进行比拟
-
比拟工作区与 HEAD 所指向分支所含文件的差别
$ git diff HEAD $ git diff HEAD -- file1 file2 # -- 为了让 git 命令读取命令参数的时候打消歧义用的,双连字符前面的是门路或文件
-
比拟 COMMIT 间的文件差别
$ git diff 5d39588 56f6198 $ git diff HEAD HEAD~1 $ git diff HEAD HEAD^1 # 上述两命令等价 HEAD 指向 5d39588 commit,HEAD~1 指向 5d39588 的父 commit 56f6198 $ git diff HEAD HEAD^1 -- readme.md #前面的 commit 为比拟基准
通过下图来看下 diff 操作时
~
与^
差别G H I J \ / \ / D E F \ | / \ \ | / | \|/ | B C \ / \ / A A = = A^0 B = A^ = A^1 = A~1 C = A^2 D = A^^ = A^1^1 = A~2 E = B^2 = A^^2 F = B^3 = A^^3 G = A^^^ = A^1^1^1 = A~3 H = D^2 = B^^2 = A^^^2 = A~2^2 I = F^ = B^3^ = A^^3^ J = F^2 = B^3^2 = A^^3^2 $ git log
|\
| * a1ef6fd (tag: C) C
| |
| \
*-. 8ae20e9 (tag: B) B
| \
| | |/
| | * 03160db (tag: F) F
| | |\
| | | * 9df28cb (tag: J) J
| | * 2afd329 (tag: I) I
| * a77cb1f (tag: E) E
- cd75703 (tag: D) D
|\
| * 3043d25 (tag: H) H
-
4ab0473 (tag: G) G
能够看出 `~` 与 `^` 都是指父节点,n 个 `~` 与 n 个 `^` 或者 `^1` 与 `~1` 都是等价的。它们都是以线性的模式来找其父节点。区别在于 `~` 与 `^` 后跟数字的情景,`~n` 仍是以线性来找父节点,但 `^n` 会按其所有的父节点来计算查找 ### 查看 commit 历史
查看以后所在分支记录
$ git log
高版本命令行会进入交互界面,如不进入交互界面间接输入日志按以下命令执行
$ git –no-page log #–no-page 禁用 page 分页,git 很多命令默认都是 page 分页的
日期格局
$ git log –date=format:%Y-%m-%d %H:%M:%S
$ git log –date=iso
$ git log –date=short
以单行简洁形式展现历史
$ git log –oneline
查看最近 5 条记录
$ git log -n5 –oneline
$ git log -n5 –oneline #”-<number>” 同 ”-n<number>”。
查看具体分支记录
$ git log temp
查看所有分支 log 记录
$ git log –all
以图形化展现记录
$ git log –all –graph
丑化记录
$ git log –color –graph –pretty=format:’%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset’ –abbrev-commit”
查看某文件中某人何时批改了何处
$ git blame <file>
### 重写 commit 历史
将本次暂存区的变更与上次 commit 的变更合并生成新的 commit,并替换 message
前提以后 commit 为最新的
$ git commit –amend # 此操作只可批改未被 push 到近程仓库的 commit 音讯
批改旧 commit 的 message 信息、合并 commit 音讯
$ git rebase -i <base> # 变基只用于本地治理未被 push 到近程仓库中代码,如果 push 到近程后又应用了 rebase,则会影响团队其余成员。
### 撤销变更
* checkout
checkout 命令用于从历史提交(或者暂存区域)中拷贝文件到工作目录,也可用于切换分支。当给定某个文件名(或者关上 - p 选项,或者文件名和 - p 选项同时关上)时,git 会从指定的提交中拷贝文件到暂存区域和工作目录。比方,`git checkout HEAD~ foo.c` 会将提交节点 *HEAD~*(即以后提交节点的父节点)中的 `foo.c` 复制到工作目录并且加到暂存区域中。(如果命令中没有指定提交节点,则会从暂存区域中拷贝内容。)留神以后分支不会发生变化。
# 工作区回退到暂存区的版本
# 指定文件
$ git checkout — file
# 所有内容
$ git checkout *
# 指定提交中文件回退到暂存区和工作目录
$ git checkout HEAD index.html
* reset(复位)
reset 命令把以后分支指向另一个地位,并且有抉择的变动工作目录和索引。也用来在从历史仓库中复制文件到索引,而不动工作目录(即 `git reset` 命令既能够回退版本,也能够把暂存区的批改回退到工作区)。它应该只被用于 _本地_ 批改, 切记不应该在和其余开发者共享的分支执行 reset。
# 挪动分支地位,并清空暂存区跟踪文件
$ git reset <commit> # 默认为 –mixed
# 挪动分支地位,并清空工作区和暂存区跟踪文件
$ git reset –hard <commit>
# 只挪动分支地位
$ git reset –soft <commit>
# 分支地位不变, 清空暂存区跟踪文件
$ git reset # 分支默认为 HEAD
# 分支地位不变, 清空暂存区指定的跟踪文件
$ git reset <file> # 分支默认为 HEAD, 把暂存区的批改撤销掉(unstage),从新放回工作区
$ git reset # 撤销所有暂存区域文件。
* revert(撤销)
在近程分支中能够通过撤销某个提交引入的更改,revert 操作形式是在最初加上一个撤销了此更改的新提交,而不是从我的项目历史中移除这个提交。这防止了 Git 失落我的项目历史,而且对于版本历史和合作的可靠性来说是很重要的。`git revert` 用于在公共的分支上撤销提交,`git reset` 用于复位本地的历史变更
因为两个命令的目标不同,它们的实现也不一样:`reset` 齐全地移除了一堆更改,`revert` 保留了原来的更改,用一个新的提交来实现撤销。
# 创立一个新的 commit,此 commit 将要 revert 的 commit 记录中的变更都撤销
$ git revert <commit>
* clean
`git clean` 命令将未被 Git 跟踪的文件从工作目录中移除。与 `rm` 作用一样,只是提供了简便形式。`git clean` 命令常常和 `git reset --hard` 一起应用, 新增的文件没有被退出暂存区,它们不会被 `git reset --hard` 影响,必须应用 `git clean` 删除它们。== 此操作请审慎应用 ==。
# 通知你哪些文件在命令执行后会被移除,而不是真的删除它。
$ git clean -n
# 移除当前目录下未被跟踪的文件
$ git clean -f
# 移除未跟踪的文件,但限度在某个门路下。
$ git clean -f <path>
# 移除未跟踪的文件,以及目录。
$ git clean -df
* reflog
`git relog` 在 HEAD 更新时(如切换分支、拉取新更改、重写历史或只是增加新的提交),援用日志都会增加一个新条目(操作记录包含对应的 commitId)。它的作用在于在执行某些撤销变更、rebase 等命令后某些 commit 记录无奈在 log 中查问到,但又须要对其复原则可应用此性能来找寻其对应的 commit 记录。### 分支与 Tag
查问本地已有分支
$ git branch
查问本地已有分支并输入详细信息
$ git branch -v
查问已存在的本地分支及近程分支并输入详细信息
$ git branch -av
创立分支
$ git branch <new-branch> # 默认以 HEAD 以后最终指向的 Commit 为基准
$ git branch <new-branch> <commitId/branch> # 已指定的 Commit 或分支为基准
删除分支
$ git branch -d <branch> # 如果执行报错(报此分支还没有被齐全 merge, 也就是还没合并到上游分支或 HEAD 上,删除会存在代码失落危险),如已确认删除对以后工程无影响,则可改用 -D 强制删除
切换分支
$ git checkout <branch>
$ git switch <branch> # 新版本 Git 提供, 此命令从字面上更容易了解,不易混同
创立并切换分支
$ git checkout -b <new-branch> <commitId/branch> # 如不指定 commitId 或 branch 默认以 HEAD 以后最终指向的 Commit 为基准
$ git switch -c <new-branch> <commitId/branch> # 新版本 Git 提供, 此命令从字面上更容易了解,不易混同
以近程分支为基准创立分支
$ git checkout –track origin/hack
应用 Tag 标识以后 commit
$ git tag <tag-name>
### merge 与 rebase
如果冀望提交历史以线性构造展现则应用 rebase,merge 操作对合并要求更宽松但展现的历史构造会简单
$ git merge <branch>
$ git rebase <branch>
终止变基操作
$ git rebase –abort
持续变基操作
$ git rebase –continue
应用配置的 merge 工具来解决抵触
$ git mergetool
解决抵触后追踪已解决的文件
$ git add <resolved-file>
解决抵触后删除已解决的文件
$ git rm <resolved-file>
### 长期紧急任务解决
工作只进行到一半,还没法提交,预计实现还需 1 天工夫。然而,必须在两个小时内修复该 bug,怎么办?幸好 Git 还提供了一个 `stash` 性能,能够把当前工作现场“储备”起来,等当前复原现场后持续工作:
stash 是堆栈构造,执行 pop 后会将最下面也就是最新存储的弹出来
➜ Git_Learning git:(rebase) ✗ git stash
Alias tip: g stash
Saved working directory and index state WIP on rebase: 3fb18e1 add test
➜ Git_Learning git:(rebase) git stash list
Alias tip: gstl
➜ Git_Learning git:(rebase) git –no-pager stash list
Alias tip: g –no-pager stash list
stash@{0}: WIP on rebase: 3fb18e1 add test
➜ Git_Learning git:(rebase)
git stash apply 只利用堆栈上最新的代码但不将其从 stash 中删除,即 apply 可屡次执行
pop 即利用代码又删除
➜ Git_Learning git:(rebase) git stash pop
Alias tip: gstp
On branch rebase
Changes not staged for commit:
(use “git add <file>…” to update what will be committed)
(use “git restore <file>…” to discard changes in working directory)
modified: index.html
no changes added to commit (use “git add” and/or “git commit -a”)
Dropped refs/stash@{0} (fdee00b4363f4a3d718ee0aa0825a52bff036d1d)
git stash list 命令显示的最左一列显示的是 stash 的序号,如 stash@{2} 和 stash@{1},序号中数字大的代表的是较早的 stash。咱们 pop 的时候能够加具体的序号,不加序号的(缺省状况下)为 stash@{0}。用法:git stash pop stash@{2} git stash pop = git stash pop stash@{0}
留神 `git stash` 不会保留工作区新增的文件(未被跟踪的),需执行 `git stash push -u -m '指定 stash 名字' `
### cherry-pick
在 master 分支上修复了 bug 后,因为 dev 分支是从 master 分支分进去的,所以,这个 bug 在以后 dev 分支上也存在。那怎么在 dev 分支上修复同样的 bug?反复操作一次,提交不就行了?为了不便操作,Git 专门提供了一个 `cherry-pick` 命令,让咱们能复制一个特定的提交到以后分支:
$ git cherry-pick 3fb18e1
[master b257a0d] fix bug
1 file changed, 1 insertion(+), 1 deletion(-)
Git 主动给 dev 分支做了一次提交,留神这次提交的 commit 是 `b257a0d`,它并不同于 master 的 `3fb18e1`,因为这两个 commit 只是改变雷同,但的确是两个不同的 commit。用 `git cherry-pick`,咱们就不须要在 dev 分支上手动再把修 bug 的过程反复一遍。### 近程仓库
显示本地与近程仓库的连贯
$ git remote
显示本地与近程仓库连贯详细信息
$ git remote -v
创立一个新的近程仓库连贯。执行 clone 操作时,会自顶创立与近程仓库的连贯,名称为 origin
$ git remote add <name> <url>
移除名为 <name> 的近程仓库的连贯。
$ git remote rm <name>
重命名近程连贯
$ git remote rename <old-name> <new-name>
将本地分支与近程分支关联
$ git branch –set-upstream <upstream> [<branchname>]
将本地分支与近程分支勾销关联
$ git branch –unset-upstream [<branchname>]
拉取近程仓库指定分支的到本地,但不与本地以后分支合并。如不指定 <branch> 则拉取近程仓库的所有分支援用
$ git fetch <remote> <branch>
拉取以后分支对应的近程正本中的更改,并立刻并入本地正本(前提本地分支与近程分支已关联 upstream)
$ git pull <remote>
等价于
$ git fetch && git merge
底层应用 rebase 合并近程分支和本地分支,而不是应用 merge。–rebase 标记能够用来保障线性的我的项目历史,避免合并提交(merge commits)的产生。很多项目组偏向于应用 rebase 而不是 merge,因为提交历史更简洁明了
$ git pull –rebase <remote>
本地仓库 master 分支推动到近程仓库的 master 分支
$ git push origin master:master #:后面的是本地分支名称,:前面的是远端分支的名称。
本地仓库 master 分支推送到远端关联分支
$ git push origin master # 前面没带远端的 master,那是因为 git 曾经为本地的 master 分支和远端的分支建设了所谓的 upstream 的关联,它晓得本地 master 对应远端的 master 分支
将以后本地仓库所在分支提交到近程雷同分支下
$ git push # origin 是缺省的 remote url;
强制推送,集成分支不举荐应用
$ git push -f
将本地所有分支推送到近程仓库
$ git push -all
推送分支时,标签不会被主动推送下来。--tags
将你所有的本地标签推送到近程仓库中去。
$ git push –tags
## Git 集成应用禁忌
后面提到过在本地分支执行 `git checkout/git reset` 时要审慎,上面的命令在多人合作的集成环境下则齐全禁止应用
* 禁止向集成分支执行 `git push -f`
* 禁止向集成分支执行变更 commit 历史的操作,如 `reset`、`rebase`
## 参考
[Git 官网教程](https://git-scm.com)
[廖雪峰 Git 教程](https://www.liaoxuefeng.com/wiki/896043488029600)
[图解 Git](https://marklodato.github.io/visual-git-guide/index-zh-cn.html)
> 文章继续更新,能够微信搜一搜「**DHC 的博客 **」第一工夫浏览,若有谬误或者不当之处,还请大家留言斧正,一起学习交换!