-
Notifications
You must be signed in to change notification settings - Fork 0
Git教程
在使用Git前我们需要先安装 Git。Git 目前支持 Linux/Unix、Solaris、Mac和 Windows 平台上运行。
Git 各平台安装包下载地址为:http://git-scm.com/downloads
在 Windows 平台上安装 Git 同样轻松,有个叫做 msysGit 的项目提供了安装包,可以到 GitHub 的页面上下载 exe 安装文件并运行:
安装包下载地址:https://gitforwindows.org/
完成安装之后,就可以使用命令行的 git 工具(已经自带了 ssh 客户端)了,另外还有一个图形界面的 Git 项目管理工具。
在开始菜单里找到"Git"->"Git Bash",会弹出 Git 命令窗口,你可以在该窗口进行 Git 操作。
Git 提供了一个叫做 git config 的工具,专门用来配置或读取相应的工作环境变量。
这些环境变量,决定了 Git 在各个环节的具体工作方式和行为。这些变量可以存放在以下三个不同的地方:
- /etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 --system 选项,读写的就是这个文件。
- ~/.gitconfig 文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 --global 选项,读写的就是这个文件。
- 当前项目的 Git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖 /etc/gitconfig 中的同名变量。
在 Windows 系统上,Git 会找寻用户主目录下的 .gitconfig 文件。主目录即 $HOME 变量指定的目录,一般都是 C:\Documents and Settings$USER。
此外,Git 还会尝试找寻 /etc/gitconfig 文件,只不过看当初 Git 装在什么目录,就以此作为根目录来定位。
$ git config --global user.name "zhangsan"
$ git config --global user.email [email protected]
如果用了 --global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。
如果要在某个特定的项目中使用其他名字或者邮箱,只要去掉 --global 选项重新配置即可,新的设定保存在当前项目的 .git/config 文件里。
要检查已有的配置信息,可以使用 git config --list 命令:
$ git config --list
http.postbuffer=2M
user.name=zhangsan
[email protected]
有时候会看到重复的变量名,那就说明它们来自不同的配置文件(比如 /etc/gitconfig 和 ~/.gitconfig),不过最终 Git 实际采用的是最后一个。
这些配置我们也可以在 ~/.gitconfig 或 /etc/gitconfig 看到,如下所示:
$ vim ~/.gitconfig
显示内容如下所示:
[http]
postBuffer = 2M
[user]
name = zhangsan
email = [email protected]
也可以直接查阅某个环境变量的设定,只要把特定的名字跟在后面即可,像这样:
$ git config user.name
zhangsan
本章节我们将为大家介绍 Git 的工作流程。
一般工作流程如下:
- 克隆 Git 资源作为工作目录。
- 在克隆的资源上添加或修改文件。
- 如果其他人修改了,你可以更新资源。
- 在提交前查看修改。
- 提交修改。
- 在修改完成后,如果发现错误,可以撤回提交并再次修改并提交。
下图展示了 Git 的工作流程:
我们先来理解下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 " 命令时,会直接从暂存区删除文件,工作区则不做出改变。
当执行 "git checkout ." 或者 "git checkout -- " 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。
当执行 "git checkout HEAD ." 或者 "git checkout HEAD " 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。
本章节我们将为大家介绍如何创建一个 Git 仓库。
你可以使用一个已经存在的目录作为Git仓库。
Git 使用 git init 命令来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行,所以 git init 是使用 Git 的第一个命令。
在执行完成 git init 命令后,Git 仓库会生成一个 .git 目录,该目录包含了资源的所有元数据,其他的项目目录保持不变(不像 SVN 会在每个子目录生成 .svn 目录,Git 只在仓库的根目录生成 .git 目录)。
使用当前目录作为Git仓库,我们只需使它初始化。该命令执行完后会在当前目录生成一个 .git 目录。
git init
使用我们指定目录作为Git仓库,初始化后,会在 newrepo 目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中:
git init newrepo
如果当前目录下有几个文件想要纳入版本控制,需要先用 git add 命令告诉 Git 开始对这些文件进行跟踪,然后提交:
$ git add *.c
$ git add README
$ git commit -m '初始化项目版本'
以上命令将目录下以 .c 结尾及 README 文件提交到仓库中。
我们使用 git clone 从现有 Git 仓库中拷贝项目,克隆仓库的命令格式为:
$ git clone <repo>
# 使用https方式
$ git clone https://github.com/MaoYanTech/tangdao.git
# 使用ssh方式
$ git clone [email protected]:MaoYanTech/tangdao.git
# 使用git方式
ssh方式需要进行相关的配置,详细可以参考 4.3 服务器上的 Git - 生成 SSH 公钥
git add 命令可将工作区的文件添加到暂存区
暂存区的代码,如果确定没有问题,可以正式提交(commit);如果发现问题,可以撤回到工作区(reset),并且不会留下记录。进可攻,退可守。
我们使用touch命令添加以下两个文件:
$ touch README.md
$ touch index.js
使用 ls 命令查看
$ ls
README index.js
使用 git status 查看此时项目的状态
$ git status
On branch master
# 表示当前分支是master
No commits yet
# 表示当前未push的commit数,此时尚没有commit
Untracked files:
# 表示没有提交到暂存区,也没有在版本库中的新文件
(use "git add <file>..." to include in what will be committed)
# 此时可以使用git add来将想要commit的文件提交到暂存区
README.md
index.js
使用 git add 命令添加两个新文件到暂存区
$ git add README index.js
# 也可以用.来代替根目录下所有改动的文件
$ git add .
使用 git status 查看项目的状态
$ git status
On branch master
No commits yet
Changes to be committed:
# 暂存区改动:
(use "git rm --cached <file>..." to unstage)
# stage即刚才的add动作,unstage即反过来,将文件还原到工作区
new file: README.md
new file: index.js
使用 git add 命令将想要快照的内容写入缓存区, 而执行 git commit 将缓存区内容添加到仓库中。
我们使用 -m 选项以在命令行中提供提交注释。-m后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
$ git commit -m "第一次版本提交"
[master (root-commit) 5b82119] 第一次版本提交
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README.md
create mode 100644 index.js
# 表示本次提交的基本信息
为什么Git添加文件需要add,commit一共两步呢?因为commit可以一次提交很多文件,所以你可以多次add不同的文件,比如:
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
git log命令可以列出当前分支下的commit记录,包括时间,用户,提交的注释等等信息
$ git log
commit 4218b35c760b941b501d73463442c72fd950dd3f (HEAD -> master)
Author: 张三 <[email protected]>
Date: Fri Jun 5 00:27:53 2020 +0800
第二次提交
index.js | 1 +
1 file changed, 1 insertion(+)
commit 5b821191c6ed6faea015643834796da36bcd3e10
Author: 张三 <[email protected]>
Date: Fri Jun 5 00:23:16 2020 +0800
第一次版本提交
README.md | 0
index.js | 0
2 files changed, 0 insertions(+), 0 deletions(-)
使用git checkout 命令来撤销工作区的改动
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
# 在这里 git 引导我们使用git checkout来丢弃工作区改动
modified: index.js
no changes added to commit (use "git add" and/or "git commit -a")
$ git checkout -- index.js
# 执行命令后没有反馈,但往往这就说明执行成功了
$ git status
On branch master
nothing to commit, working tree clean
git checkout -- file命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令
有两种情况: 一种是index.js自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;
一种是index.js已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。
使用 git reset HEAD 将已添加至暂存区的文件撤销回工作区
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: index.js
no changes added to commit (use "git add" and/or "git commit -a")
$ git reset HEAD index.js
Unstaged changes after reset:
M index.js
首先查看git log
$ git log
commit 4218b35c760b941b501d73463442c72fd950dd3f (HEAD -> master)
Author: 张三 <[email protected]>
Date: Fri Jun 5 00:27:53 2020 +0800
第二次提交
index.js | 1 +
1 file changed, 1 insertion(+)
commit 5b821191c6ed6faea015643834796da36bcd3e10
Author: 张三 <[email protected]>
Date: Fri Jun 5 00:23:16 2020 +0800
第一次版本提交
README.md | 0
index.js | 0
2 files changed, 0 insertions(+), 0 deletions(-)
# 撤回到上一次commit
在Git中,用HEAD表示当前版本,也就是最新的提交
上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100
$ git reset --hard HEAD^
HEAD is now at 5b82119 第一次版本提交
有的同学使用zsh会报错
$ git reset --hard HEAD^
zsh: no matches found: HEAD^
# 添加转义符即可
$ git reset --hard HEAD\^
也可以直接使用commit id
git log中,一大串类似4218b35...的是commit id(版本号),和SVN不一样,Git的commit id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示,而且你看到的commit id和我的肯定不一样,以你自己的为准。
为什么commit id需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向一个commit id,变为指向另一个commit id,然后顺便把工作区的文件更新了。所以你让HEAD指向哪个版本号,你就把当前版本定位在哪。
所以,如果你不小心reset到了一个错误的版本,可以使用
$ git reset --hard <commit id>
怎样找到一个log中已经不存在的commit id呢,就要用到reflog
git reflog 是一个非常有用的命令,可以展示已经执行过的所有动作的日志。包括合并、重置、还原,基本上包含你对你的分支所做的任何修改。
如果你犯了错,你可以根据 reflog 提供的信息通过重置 HEAD 来轻松地重做!
$ git reflog
5b82119 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
4218b35 HEAD@{1}: commit: 第二次提交
5b82119 (HEAD -> master) HEAD@{2}: commit (initial): 第一次版本提交
每一行代表一次对分支的修改操作,修改后的commit id就放在第一列。所以我们很容易找到之前操作过的commit id,来进行还原操作。
参考下面两篇教程,写的非常好
更多教程,请参考
对照下面的表格和优先级检验自己的学习成果,如果有遗漏可以针对遗漏的地方进行学习。这里列出的要求是最基础的要求,可以帮助你顺利地完成入职前期的基本工作。但是,学无止境,可以在掌握了这些以后继续深入学习。
命令 | 学习要求 |
---|---|
clone | 会将远程仓库克隆到本地; |
branch | 会查看本地所有分支;新建分支;删除本地 / 远程分支;分支改名; |
checkout | 会切换分支;还原当前工作区的改动;将某个文件改动成某个版本;在当前分支基础上建立新分支; |
add | 理解暂存区概念;会添加文件 / 添加所有文件到暂存区; |
commit | 会提交commit;会附带messaage;会将本次待提交内容和最近一次commit合并; |
push | 会将本地commit推动到远程;会处理常见的分支错乱情况【题目】;会使用本地分支强制覆盖远程分支; |
pull | 理解pull的原理;会使用远程分支强制覆盖本地分支; |
merge | 理解merge的原理;理解冲突的原理,会解决冲突;理解(no-)fast-forward merge模式; |
命令 | 学习要求 |
---|---|
stash | 会使用stash进行暂存和恢复;会管理暂存列表 |
reset | 会将分支顶部还原到某个commit节点 |
log | 理解log的意义,用途 |
reflog | 理解reflog的意义,用途 |
config | 会用config命令配置用户名,邮箱等信息 |
命令 | 学习要求 |
---|---|
fetch | 理解这个命令的主要作用,知道和merge的区别 |
diff | 理解这个命令的主要作用 |
cherry-pick | 会使用cherry-pick进行commit的pick |
revert | 理解revert命令的意义,会使用revert进行代码回滚 |
rebase | 理解rebase命令的意义,知道和merge的区别 |