Git快速入门
Git是一个版本控制工具,通常配合远程代码仓库多人协作开发。上手Git并不难,用过之后就会觉得真香。我入门的方式就是给一个项目提Pull Request。
git学习思路:单链 -> 树 -> 多棵树
本地版本控制(利用状态机的思想学习Git)
分支版本控制(利用树的思想)
远程仓库控制(两颗树之间的对应!)
最后学习学习git相关的配置文件,git就算简单入门了。
推荐阅读:https://www.progit.cn/#_pro_git
在线Git闯关-图形化学GIt:https://learngitbranching.js.org/?locale=zh_CN
一、git基础
工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪
已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录。
未跟踪文件是工作目录中除已跟踪文件以外的所有其它文件,它们既不存在于上次快照的记录中,也没有放入暂存区。
经过Git追踪的文件,在工作一段时间后,它们可能处于其中之一的状态:
- committed 已经提交,数据在本地仓库中
- modified 已经修改,没有保存在数据库中
- staged 已经暂存,包含在下次提交的快照中
引入Git项目三个工作区域的概念:Git仓库、工作目录、暂存区域
基本的 Git 工作流程如下:
- 在工作目录中修改文件。
- 暂存文件,将文件的快照放入暂存区域。
- 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
如果 Git 目录中保存着的特定版本文件,就属于已提交状态。 如果作了修改并已放入暂存区域,就属于已暂存状态。 如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。
二、本地版本控制
2.1 获取仓库
获取仓库的方法:
- 现有目录初始化仓库
- 克隆仓库
(1)现有目录初始化仓库
1 | git init |
(2)克隆仓库
1 | git clone [url] |
比如
1 | git clone https://github.com/libgit2/libgit2 |
这会在当前目录下创建一个名为 “libgit2” 的目录,并在这个目录下初始化一个 .git
文件夹。
如果你想在克隆远程仓库的时候自定义本地仓库的名字, 在上条命令后面跟着你自定义的名字:
1 | git clone https://github.com/libgit2/libgit2 your_local_name |
2.2 git状态
工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪
已跟踪的文件是指那些被纳入了版本控制的文件,在上一次快照中有它们的记录,在工作一段时间后,它们的状态可能处于未修改,已修改或已放入暂存区。
未跟踪文件是工作目录中除已跟踪文件以外的所有其它文件,它们既不存在于上次快照的记录中,也没有放入暂存区。
初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修改状态。
编辑过某些文件之后,由于自上次提交后你对它们做了修改,Git 将它们标记为已修改文件。
我们逐步将这些修改过的文件放入暂存区,然后提交所有暂存了的修改,如此反复。这就是Git本地工作的思想。
2.3 git操作
(1)查看文件状态
1 | $ git status |
(2)跟踪新文件(untracked -> staged)
1 | $ git add yourfile |
这是个多功能命令:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。
(3)暂存已修改文件 (modified -> staged)
1 | $ git add yourfile |
(4)忽略文件
在这种情况下,我们可以创建一个名为 .gitignore
的文件,列出要忽略的文件模式。这些文件不会被提交。
(5)提交更新
1 | $ git commit |
可以看到,提交后它会告诉你,当前是在哪个分支(master
)提交的,本次提交的完整 SHA-1 校验和是什么(463dc4f
),以及在本次提交中,有多少文件修订过,多少行添加和删改过。
在提交前,请一定要确认还有什么修改过的或新建的文件还没有
git add
过,否则提交的时候不会记录这些还没暂存起来的变化。 这些修改过的文件只保留在本地磁盘。每次准备提交前用
git status
看下,是不是都已暂存起来了。
(6)跳过暂存
1 | git commit -a |
跳过暂存步骤,将已跟踪的文件(若已经修改)提交。
(7)移除文件
xx
(8)版本回滚
首先查看提交的各个版本提交的 SHA-1 标识符。两个命令都行,不过第二个显示的信息更加简洁。
1 | $ git log --pretty=oneline |
使用 git revert
命令可以创建一个新的提交,它撤销了指定的提交内容,但是保留了原来的提交记录。
1 | git revert SHA-1 |
使用 git reset
命令可以回滚提交,但这种方式是破坏性的,因为它会更改 Git 历史记录,从而删除要回滚的提交以及回滚之后的提交。
1 | git reset --hard SHA-1 |
使用reset后就不能恢复了!
(9)放弃暂存区的修改,回到上次提交
要放弃本次代码修改并将工作区回到上次提交的状态,可以使用命令:
1 | git checkout -- . |
该命令会将工作区中所有文件的修改全部还原到上次提交的状态,注意命令中的点号”.”表示当前目录,也可以替换成具体的文件或目录名。
此外,如果你只是想还原某个特定文件的修改,可以使用:
1 | git checkout -- 文件名 |
注意,这个命令并不会从版本库中删除已经提交的修改记录,只是还原到上次提交。
如果需要完全撤销某个提交,使用 git reset 命令。
三、分支版本控制
3.0 查看分支
1 | git branch |
这个命令可以查看当前的所有分支。
1 | $ git branch |
注意 master
分支前的 *
字符:它代表现在位于的分支(当前 HEAD
指针所指向的分支)。
3.1 查看分支指向的对象
分支指向的对象指commit的文件对象。
1 | git log --oneline --decorate |
git log --oneline --decorate
输出效果:
1 | 位于分支 master |
当前 HEAD
指针所指向的分支是master
,代表当前所在分支。
3.2 创建分支
1 | git branch your_name |
这会在当前提交对象上创建your_name的一个指针,代表创建了一个分支。
3.3 切换分支
1 | git checkout branch_name |
切换到branch_name这条分支。实际上会将head指针指向branch_name上。输出效果:
1 | 2348425 (HEAD -> testing, master) v2 |
(1)当你在testing分支提交时,会像现在这样:
1 | dcd85fe (HEAD -> testing) add file a.c |
(2)当你切换回mater分支时,head指针会指向master。
注意:你的工作目录恢复成 master
分支所指向的快照内容。 也就是说,你现在做修改的话,项目将始于一个较旧的版本。 本质上来讲,这就是忽略 testing
分支所做的修改,以便于向另一个方向进行开发。
(3)当你在master分支上再次提交修改时,你的项目就会产生分叉!
1 | git log --oneline --decorate --graph --all |
它会输出你的提交历史、各个分支的指向以及项目的分支分叉情况。
1 | * c8f77ad (HEAD -> master) somethign fixed |
Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。
3.3 分支工作流
(1)创建并切换到新分支
1 | git checkout -b issue54 |
(2)删除分支
1 | git branch -d hostfix |
(3)合并分支
1 | //先切换到master分支 |
和之前将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。 这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。
3.4 遇到冲突时的合并
如果两次合并都涉及同一个文件的同一处修改,在合并它们的时候就会产生冲突。
此时需要手动排除冲突。步骤:
- git status 查看冲突的文件
- 逐一打开冲突文件修改
- git add将修改完的文件放入暂存区
- git commit提交修改
3.5 放弃合并
1 | git merge --abort |
有时候冲突太多了,直接放弃合并吧。
3.6 分支开发工作流
比如只在 master
分支上保留完全稳定的代码——有可能仅仅是已经发布或即将发布的代码。 他们还有一些名为 develop
或者 next
的平行分支,被用来做后续开发或者测试稳定性——这些分支不必保持绝对稳定,但是一旦达到稳定状态,它们就可以被合并入 master
分支。
稳定分支的指针总是在提交历史中落后一大截,而前沿分支的指针往往比较靠前。
四、远程仓库控制
4.1 查看远程仓库
1 | $ git remote |
它会列出你指定的每一个远程服务器的简写。默认情况下就只有一个origin。
使用选项 -v
,会显示远程仓库使用的 Git 保存的简写与其对应的 URL。
1 | $ git remote -v |
4.2 添加远程仓库
1 | git remote add <shortname> <url> |
添加一个新的远程 Git 仓库,同时指定一个你可以引用的简写。
比如:
1 | $ git remote |
先查看现在有远程仓库,只有一个origin。然后添加一个简写为pb的远程仓库。再此查看对应的远程仓库,发现pb已经添加上了。
4.3 从远程仓库抓取fetch
1 | git fetch [remote-name] |
这个命令会访问远程仓库,拉取所有你还没有的数据(包括新的分支)。
git fetch
命令会将数据拉取到你的本地仓库 - 它并不会自动合并或修改你当前的工作。
如果你使用
clone
命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以 “origin” 为简写。 所以,git fetch origin
会抓取克隆(或上一次抓取)后新推送的所有工作。
4.4 推送到远程仓库push
1 | git push <远程仓库名> <本地分支名>:<远程分支名> |
将你本地的某个分支推送到远程的某个分支。两个分支名可以不相同。
如果一个本地分支和远程分支建立了联系(下节会讲,所谓联系就是一一对应),直接
git push
就能推送到对应的远程分支。
4.5 跟踪分支track
跟踪实际上意味着将本地分支与远程分支建立联系。
查看本地分支与远程分支之间的关系
1 | git branch -vv |
样例输出:
1 | develop 3e7f1c3 [origin/develop: ahead 2] Fix bug #123 |
这个输出列出了本地仓库中的三个分支:develop、feature-abc和main。其中,星号(*)表示当前所在的分支是main。
对于每个分支,输出显示了以下信息:
- 分支名称
- 分支所依据的提交哈希值
- 与之对应的远程分支名称及其状态信息
在这个示例中,develop分支有两个本地提交尚未推送到远程,而feature-abc分支只有与远程分支同步的提交。同时,main分支有一个本地提交,需要将其推送给远程,并且还需要从远程拉取两个提交。
1、当克隆一个仓库时,git通常会自动地创建一个跟踪
origin/master
的master
分支。2、需要重点注意的是这个命令并没有连接服务器,它只会告诉你关于本地缓存的服务器数据。
如果想要统计最新的领先与落后数字,需要在运行此命令前抓取所有的远程仓库。
git fetch --all
跟踪一个远程分支(重要)
1 | git checkout -b [local_branch] [remote_name]/[remote_branch] |
这个命令会自动创建一个本地分支并跟踪远程分支。
这是一个十分常用的操作,所以 Git 提供了 --track
快捷方式:
1 | git checkout --track origin/[remote_branch] |
直接创建本地的同名分支,跟踪远程分支。比如:
1 | git checkout --track origin/bugfix-update-v1.1 |
这会建立一个本地分支origin/bugfix-update-v1.1
,它会自动与远程同名分支建立联系。
修改本地分支跟踪的远程分支
1 | git branch -u [remotename]/[remote_branch] |
这条命令将设置当前分支跟踪的远程分支。
或者这条命令也有同样效果。
1 | $ git branch --set-upstream-to=[remotename]/[branch] |
4.6 拉取分支pull
1 | git pull |
git pull是指将远程仓库的代码更新到本地仓库,并合并到本地分支上。
具体来说,git pull命令相当于执行了两个命令:
- git fetch:从远程仓库拉取最新的代码,并将其存放在本地的“FETCH_HEAD”引用中,但不会修改本地分支。
- git merge:将“FETCH_HEAD”引用合并到当前分支上,从而将最新的代码更新到本地仓库。
当执行git pull命令时,会自动执行以上两个步骤,从远程仓库拉取最新的代码,并合并到本地分支上,以使本地代码与远程仓库保持同步。
4.6 查看远程仓库
查看某一个远程仓库的更多信息
1 | git remote show [remote-name] |
重命名引用的名字
1 | git remote rename [old_name] [new_name] |
比如将pb远程引用的仓库名称改为paul
1 | $ git remote rename pb paul |
五、标签
Git 可以给历史中的某一个提交打上标签,标签可以看作是提交的一个快照,但它通常用于标记重要的里程碑,如发布新版本。与分支(branch)不同,标签是静态的,指向特定提交,不会随着新的提交而移动。
(1)A tag is immutable!
Tag是不可变的,在合入主分支与发布的期间,不必担心任何非预期的改变。
(2)便于管理
标签可比提交带有更多的信息。
5.1 标签使用
(1)创建标签
1 | git tag v1.0 |
这会为当前HEAD指向的提交打上v1.0
的标签。
(2)查看标签
1 | git tag -l |
如果你没有查看到想要的标签信息,不妨
1 | git fetch --tags |
这个命令会从远程仓库获取所有最新的tags。
(3)检出特定标签
1 | git checkout -b <new_branch_name> tags/<tag_name> |
基于标签名称,创建新分支。
你不能直接“拉取”一个tag,但你可以通过检出tag或基于tag创建一个新分支来查看和使用特定版本的代码。