概述

本文是学习Git官方文档Git Pro的笔记和记录。

什么是Git

Git是一种用于代码管理的分布式版本控制工具,可以实现大型软件项目的多人协作。

Git的出现是为了解决大型软件项目开发过程中遇到的一些问题:

  • 在代码编写的过程中发现新的代码实现存在一定的问题,需要回退到之前的版本时怎么办?

    编译器的撤销快捷键可能有一点用,但是这种方式能存储的撤销步数有一个上限且每次关闭IDE之后这些短暂的记忆就消失了。仅能适用于临时的一些回退。

  • 多人同时修改一个文件怎么进行代码的合并、并能有效处理分布式开发的源码冲突? 串行的方式可以避免冲突但是开发效率上低的可怕

  • 想在已经稳定的版本上开发新的功能害怕把已有的代码版本破坏应该怎么办? 手动的复制需要人工管理繁琐的版本、麻烦且不直观

为了解决这个问题,Linus Torvalds设计了Git解决这一问题。

Git的需求来源于Linux的开发过程:Linux是一个由社区驱动的开源项目,其中有一部分秉持自由软件思想的开发者对Linux使用商用闭源的代码管理工具十分不满(说的就是你GNU和Richard Stallman)。他们对现有的代码管理工具进行修改导致厂商收回了授权。最终,Linus决定自己开发一个代码管理工具并将其命名为git——意为“愚蠢的”(是想讽刺谁呢,好难猜啊)。 尽管Linus是Linux(或者说GNU/Linux)项目最早的开发者,但其仅负责Linux系统内核(kernel)的开发和维护。在社区中,GNU基金会作为自由软件思想的先锋为Linux系统做出了卓绝的贡献(GCC、GDB等等)。但因为理念和其他的冲突(开源和自由软件之争、GNU/Linux的命名权争议等),Linux社群中也常常存在矛盾。某种程度来讲,git就诞生于Linux社群内部的矛盾。

Git是怎么工作的

Git的设计继承了许多以前VCS(版本控制系统)的方法,但是和CVS等传统的集中式版本控制系统(CVCS)有着本质的不同。

  • Git基于快照机制进行文件存储

    在 Git 中,每当你更新代码并想要将其提交到本地仓库时,它会对当时的全部文件创建一个快照(称为一个提交commit)。在提交存储到代码仓库之前,git会计算一个校验和(SHA-1散列)作为索引,并保存这个快照的索引(commmit的哈希值)。

    每个用户在本地都拥有代码仓库的完整备份(一连串的文件快照/提交链),而不是仅仅拥有一个快照(这是CVCS的方式)。

    如果提交时某文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。

  • Git进行分布式的版本管理

    当需要提交时,用户需要设定一个远端仓库(一般是服务器里的裸仓库)然后由用户通过各种分布式的操作保持本地仓库和远程仓库的一致性。

    远端仓库一般以裸仓库的形式在服务器上存放,裸仓库没有工作目录不能直接进行代码的修改。非裸仓库具有工作目录,可以直接进行代码修改,但是一般不能作为Git push的对象。

    在实际的工程实践中,往往使用一个拥有所有代码仓库的Git服务器进行版本控制。每个用户都把Git 服务器中代码仓库(裸仓库形式)设置为远程仓库。 尽管如此,Git依然进行的是分布式的版本控制——中心节点崩溃以后,任何一个和之前的代码仓库保持了同步的仓库都可以作为新的仓库(git仓库保存的是完整的备份)。

  • Git的文件管理和版本控制

    在Git中,文件有几种状态:未追踪、已修改Modified、已暂存Modified、已提交Committed。

    未追踪的文件、已修改的文件在工作目录(Working Tree)之中需要通过git add保存到暂存区(Staging Area)。暂存时,git可以灵活的控制每一个文件(甚至是每一个代码块)是否需要暂存,然后进行统一的提交。最终实现灵活的部分提交。

    在确认过暂存区的更改之后,可以将使用git commit将已暂存的文件提交到代码库(Repository)的分支(branch)。

    在代码仓库里,往往具有一个代码的许多分支(branch), 用来管理代码的不同版本。

Git本质

Git本质上是一个键值对的数据库,值是一个数据对象,键是根据数据计算的SHA1哈希值。

Git中的数据对象有四种:

  • 存储文件内容的Blob对象:

    Blob对象是实际的存储载体,是某个文件内容的快照。根据数据内容具有唯一的HASH值,不具有文件名。对文件修改后就会创建新的Blob对象,相当于新文件的一个快照。Git的实际存储是快照式的,不是增量式的

  • 存储目录信息的Tree对象

    Tree对象是用来描述目录信息,具有指向Blob或者其他Tree的指针。根据Tree对象和Blob对象,可以恢复目录和其下的文件某一时刻下的状态。

  • 存储每一次提交的内容,构成数据历史的Commit对象

    一次提交由提交信息(提交者、提交的名称描述、时间等等)、Tree对象(描述当前提交下的目录情况)、指向父提交的指针(用来组成提交历史)

  • 存储永久指向某个提交的指针的Tag对象

    git add会在暂存区创建对应的blob对象,git commit会创建Tree对象和Commit对象

通过这四种对象,Git可以将提交组织成一张有向无环图。分支实际上就是指向某一个提交的指针,特别的,\HEAD是当前所在的指针。这个有向无环图很可能没有覆盖数据库中的所有的键值对,这些不在提交历史中记录的提交称为游离态提交,是使用时应该避免的。

git 使用

1.仓库操作

git init:

git init 初始化仓库 git init --bare --shared 以裸仓库的形式初始化仓库,允许其他用户修改裸仓库(提交远程分支)

git clone:

git clone

git remote:

git remote

git fetch:

git fetch

git pull:

git pull

git push:

git push

2.暂存相关操作

git add:

git add . 暂存当前目录的文件到暂存区

git clean:

git clean -filename 删除未追踪的文件和路径

git stash:

临时的存储文件系统和暂存区的文件到一个栈,当前的状态切换到HEAD的提交状态。不保存未追踪文件,可以使用-u带上 git stash list 列出已保存的改动

git stash push -m "描述" 保存当前改动(压入栈中)

git stash pop 弹出已保存的改动,恢复到文件系统和暂存区(出栈并销毁)

git stash apply 应用某个已保存的改动,恢复到文件系统和暂存区(不出栈,还可list查看到)

git stash drop 删除某个已保存的改动

git worktree

git worktree

3.提交相关操作

git commit : 提交暂存的文件到当前所在分支

git commit -m "commit title" -m "commit introduce" 在当前分支提交修改
git commit --amend 修改上一次提交的内容,记录里仅仅保存一个提交

git reset : 撤销提交(commit)或者暂存(add) 的文件

git reset HEAD~1 将所在分支最近的一个提交,保存其修改在文件系统(撤销commit、add) git reset --soft HEAD~1 将所在分支最近的一个提交,保存其修改在暂存区(撤销commit) git reset --hard <commit-hash> 撤销将对应哈希的提交及之后的提交,不保存修改。用于彻底放弃提交

git reset 可以reset到之后的提交! 因为git reset 本质是移动head指针到某个提交,然后不追踪之后的所有提交历史

git revert 通过创建一个反向的新提交来撤销之前的提交。之前的提交也在git log中

git revert <commit-hash>

4.分支相关操作

git branch

git branch <branch-name> 创建一个分支

git merge

git merge <branch-name> git merge --abort

git rebase

5.查看信息

git status 查看文件的状态 git diff 查看工作目录和暂存区的文件差异
git diff --cache 查看暂存区和所在分支最近的提交之间的差异
git log 查看仓库的提交日志 (git graph能看到的、可见的提交) git log --oneline --graph --all 以图形化形式查看,每个提交最短的一行描述显示 git reflog 查看操作日志(包括所有的撤销操作)

git的设置

git的SSH公钥设置

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
cat ~/.ssh/id_rsa.pub