25

三分钟快速搞定 Git 常规使用

 4 years ago
source link: https://mp.weixin.qq.com/s/L3OgVfyGabJPz3M-2CTCLw
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

git是什么?

git是一个免费且开源的分布式版本控制系统,可以快速高效地应用于从小型到大型的项目,其实就是一个高级一点的版本控制系统。

为什么要使用git

学一门技术之前,一定要弄清楚为什么要学,不能为了跟随潮流去被动学习。什么大家都在用了,要是我不会岂不是out了,这种想法too young too simple。

首先,简单了解一下git的由来。Linux内核开源项目从2002年开始使用名为BitKeeper的商业化版本控制工具,到了2005年,开发BitKeeper的商业公司BitMover同Linux内核开源社区的合作关系结束,他们收回了Linux内核社区免费使用BitKeeper的权力。这就迫使Linux开源社区(特别是Linux的缔造者Linus Torvalds)基于使用BitKeeper时的经验教训开发出了git。

接下来来弄清楚为什么要学git,这就要从版本控制系统说起。版本控制系统,英文翻译是Version Control System,简称VCS,是一种记录一个或若干文件内容变化,以便将来查阅特定版本变更情况的系统。随着时间的推移,版本控制系统的发展被划分为三个阶段:本地版本控制系统、集中化的版本控制系统和分布式版本控制系统。

本地版本控制系统是在本地采用某种简单的数据库来记录文件的历次变更差异,以便在未来可以查看特定的版本。虽然它可以解决版本丢失和覆盖的问题,但是它最大的问题就是只能在本地使用,不能协同工作,而且如果本地数据库数据损坏或丢失,那么对应的历次变更记录也就完全丢失了。 ZjyEzqz.png!web

集中化的版本控制系统,例如像CVS,SVN等,有一个集中管理的服务器,所有开发人员通过客户端连到这台服务器,取出最新的文件或者提交更新,管理员可以掌控每个开发者的权限。集中化的VCS虽然解决了版本控制问题,还可以多人协作,但缺点就是太依赖于远程服务器,VCS服务器宕机后,会影响所有人的工作,并且在没有备份的情况下,会有数据丢失风险。 yu222qI.png!web

任何事物的存在一定有其存在的道理,而分布式版本控制系统的出现就是为了解决之前存在的问题。例如像git这样的分布式版本控制系统,客户端并不只提取最新版本的文件,而是把代码仓库完整地镜像下来。每一次的提取操作,实际上都是一次对代码仓库的完整备份。所以并没有“中心服务器”的概念,所谓的“git服务器”,也同每个人的电脑一样,只是为了多人协作时,方便大家交换数据,支持离线工作之用。不过,它也不完美,缺点就是相对复杂,而且代码保密性差,因为每个人的本地存的都是全量的变更记录和文件。 J3yue2m.png!web

就目前来说,svn和git是使用最多的版本控制系统了,下面用一张图来展示它们之间的优缺点,至于为什么要使用git这里就不再做总结了。 Aja2Avj.jpg!web

git基础理论

如果你使用过svn,那么你一定知道svn管理的每个文件都有自己单独的版本快照,而git在每个版本中,以当时的全部文件为单位,记录一个快照。

在将文件保存到git时(简单理解就是使用commit操作时),所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。git使用SHA-1算法计算数据的校验和,通过对文件的内容或目录的结构计算出一个SHA-1哈希值,作为指纹字符串。该字串由40个十六进制字符组成,例如aa73ba5a19222487a35bcf788809cf1d197d7d06,所有保存在git数据库中的内容都是用此哈希值来作索引的,而不是靠文件名。

对于任何一个文件,在git内都只有三种状态:已提交(committed)、已修改(modified)和已暂存(staged)。已提交表示该文件已经被保存到本地仓库了,例如使用commit操作后的状态;已修改表示本地仓库中的文件被修改了,但还没有提交保存,例如已经被git管理的文件被修改了,但是还没使用add操作或commit操作时的状态;已暂存表示把已修改的文件放在下次提交时要保存的清单中,例如使用add操作将文件添加到暂存区,等待使用commit操作提交保存到本地仓库中。

git中的三个工作区域:工作目录、暂存区域和本地仓库。工作目录,可以理解为本地看得到的、实际可操作的目录空间,例如文件clone到本地后本地的目录。本地仓库用于存储这些变更的记录及文件。暂存区域就是新修改或新添加的文件暂时存储的空间,这个区域的文件等待被提交保存到本地仓库。下面的图展示了它们三者之间的本地转换关系。 RnAV3ai.jpg!web

git是如何存储我们的操作记录和文件的呢?这里就需要了解一下git内部的三个对象:blob对象、tree对象和commit对象。blob对象用来存储文件的内容,它的键是使用SHA1算法生成的。tree对象可以看作一个目录,管理其它tree对象或是blob对象,可以把它们看做子目录和文件,tree对象内部存储指向blob对象或是其它tree对象的指针,一般用来表示内容之间的目录层级关系。commit对象存储tree对象的指针,并记录提交者信息、原作者信息、时间戳和提交的注释,同时还存储上一次提交的commit对象指针等(第一次提交不存储上一次提交的commit对象指针)。下面用一张图展示它们之间的关系。 Q3QnEj6.jpg!web

每次的commit操作都会对应一个版本快照,同时commit对象会包含一个指向上次提交对象(父对象)的指针,下面的图可以很好的辅助理解。 rMjMVra.jpg!web

git分支

git中的分支,其实本质上仅仅是一个指向commit对象的可变指针。这句话,如果刚接触git可能不是很好理解,还是看下面的图。 AfAF3ii.png!web

上面的图中有三次commit,每次commit都对应一个当时的版本快照。最后一次commit是f30ab,此时有一个master指向了它,这个master所代表的就是一个分支,它是主干分支,如果此时以f30ab为基线创建了一个testing分支,那么testing也会指向f30ab,分支在git中就是一个可变的指针。 6jaqaeB.png!web

每个分支都是相互独立的,例如master和testing,变更master分支中的内容,不会影响testing分支中的内容,不要被上面图片所迷惑,它们俩只是刚开始的时候指向了相同的commit对象,前面的变更内容都是相同的,因为都是以f30ab为基线的,但是各自分支上的变更操作只影响对应分支的内容,切换到不同的分支也只能看到各自分支的内容。

git中有一个HEAD指针,它总指向当前所在的本地分支,git中分支的切换就是移动HEAD指针,指向对应的分支,例如下图中HEAD指针指向了master分支,表示当前在master分支。 MJraYj2.png!web

又例如下图中,HEAD指向testing分支,表示工作空间当前已经切换到了testing分支。 B73uiq2.png!web

有分支就会有分支合并,下面用图来展示git中的分支合并。下图中master主干分支上有C0、C1、C2和C4四次提交,同时以C2位基线创建了iss53分支,并且在iss53分支上做了C3和C5两次提交。 UfAj6zj.png!web

此时如果直接把iss53分支合并到master主干分支,就会生成一个合并提交C6,并且此时master指向这个commit对象,这就是分支合并。 YZVji2f.png!web

远程仓库和远程分支

git本地仓库存储了所有的文件和操作记录,git在本地的这些版本镜像和操作记录,如果没有push,最终也只是保存到了本地的仓库中,push必须要在远程git服务器上有一个相应的远程仓库,这个远程仓库的结构和你本地仓库是一样的,这样其他人就可以通过这个远程仓库下载你在本地操作后的变更了。git默认的远程仓库名称是origin,origin与分支名字master一样,在git中都并没有任何特别的含义一样,仅仅是因为使用习惯了。

本地分支如果想推送到远程git服务器,必须要在远程git服务器上有一个相应的远程分支接应,这样远程分支就包含了本地分支推送的内容了。

git安装及常规使用

git安装很简单,浏览器输入git-scm.com/download,选择对应操作系统的安装包下载,然后一步一步安装即可。安装完成后,就可以使用git了。对于初学者来说,推荐使用终端命令行来操作,这样对你学习git有很大的帮助,因为命令会让你不断的去思考这一步做了什么,有什么结果,下一步该做什么,你会不断的去探索git更深入的技能和原理。反之,上来就使用图像化的界面工具,使用的再熟练,你也只是这个工具用的很熟,不会有深入了解的想法。

对初学者来说,能熟练掌握常规使用命令就可以完成大部分的git工作了。常规操作:使用clone命令将远程仓库内容下载到本地,使用checkout命令切换分支,在分支上变更(修改、新增或删除)本地文件后,使用add命令将变更操作的文件添加到暂存区,然后使用commit命令将暂存区的内容提交到本地仓库,最后使用push命令将本地仓库中分支的变更推送到远程仓库的分支上。如果要拉取远程仓库上的最新内容,使用pull命令;如果要合并分支,使用merge命令;如果要撤销操作,使用reset命令。

git命令有不少,如果你对某个命令不是很熟悉或者忘记了某个命令的参数是什么,可以使用git help命令来查看相关的使用帮助信息,命令格式如下:

git help <verb>  或 git <verb>  --help  或 man git-<verb>

其中, 代表具体的命名名称,例如config命令,git help config。这个命令在Linux或Mac os上会输出命令的帮助信息,在Windows系统上会打开一个网页显示帮助信息。

对于初次使用git来说,你需要配置用户的用户名和用户邮箱,这样在后续使用git的时候,git会使用这个用户名和邮箱来记录是谁谁谁干了什么,配置全局的用户信息,具体命令如下:

git config --global user.name zhangsanfeng #配置用户名是zhangsanfeng
git config --global user.email [email protected] #配置用户邮箱是[email protected]

配置好git用户信息后就可以进行后续的git操作了(当然在进行后续的操作之前可能需要生成SSH公钥和授权等,这里就不做详细的解说了)。要想将远程仓库内容下载到本地目录中,需要使用git clone命令,具体命令如下:

git clone [email protected]:user/demo.git

上面的命令会在当前目录下创建demo目录,并下载[email protected]服务器上demo.git内容

git clone [email protected]:user/demo.git demos

这个命令在当前目录下创建demos目录,并下载[email protected]服务器上demo.git内容

查看当前工作空间中文件变更的状态,命令行输入:

git status

这个命令会显示有没有变更,哪些文件变更了,处在什么状态,同时还会有相应的操作提示。

将文件的变更添加到暂存区,命令行输入:

git add file_name #将指定文件的变更添加到暂存区
git add file1_name file2_name  #将指定文件的变更添加到暂存区
git add *.java  #使用通配符将符合规则的文件变更添加到暂存区
git add .     添加当前目录及子目录下的所有变更到暂存区
git add --all 添加所有的变更到暂存区
git add -A    添加所有的变更到暂存区

查看文件之间的变更差异,命令行输入:

git diff 查看未暂存和暂存区域快照之间的差异
git diff --staged 查看已暂存和已提交快照之间的差异
git diff HEAD 显示工作区与当前分支最新commit之间的差异

将文件的变更提交到本地仓库,命令行输入:

git commit [file1] [file2] ... -m [message]  #[message]标注提交的注释
git commit -a –m “xxxxx”  #跳过暂存区直接commit,xxxxx标注提交的注释
git commit --amend -m [message]  使用一次新的commit,替代上一次提交,如果代码没有任何新变化,则用来改写上一次commit的提交信息

将本地仓库的变更推送到远程仓库,命令格式如下:

git push <远程仓库名> <本地分支名> <远程分支名>
git push origin develop:dev  #将develop分支推送到远程origin仓库的dev分支
git push origin develop  #将develop分支推送到远程origin仓库的develop分支
git push origin :dev   #删除指定的远程分支
git push origin   #将当前分支推送到远程origin仓库的对应分支
git push   #当前分支只有一个远程分支,主机名都可以省略

拉取远程仓库的变更到本地(不主动合并),命令行输入:

git fetch  #命令从服务器上抓取本地没有的数据,但它并不会修改工作目录中的内容,需要手动合并
git fetch <远程仓库名>  #将某个远程仓库的更新全部取回本地
git fetch <远程仓库名> <分支名>  #将某个远程仓库的某个分支的更新取回本地

分支合并,命令行输入:

git merge xxx  #将xxx分支上的代码合并到当前分支

拉取远程仓库的变更到本地(主动合并),命令行输入:

git pull [remote] [branch] #remote为远程仓库,branch为远程分支,这两个参数都可以省略,省略则默认拉取已经追踪的远程仓库的远程分支的文件

这里注意下,pull命令会主动将远程分支的内容合并到本地分支,所以git pull = git fetch + git merge

丢弃未加入到缓存区(就是未使用git add命令)的修改,命令行输入:

git checkout -- filename  #丢弃某个文件的变更
git checkout filename  #丢弃某个文件的变更
git checkout .  #丢弃所有文件的变更

切换分支,命令行输入:

git checkout 分支名
git checkout -b 新分支名  # 如果分支不存在则新建

删除git中内容,命令行输入:

git rm xxx #从已跟踪文件清单中移除,会删除文件
git rm –f xxx #强制删除已暂存中的修改
git rm --cache xxx #删除暂存区中的修改(不再追踪),不删除文件,且保留变更
git rm log/\*.log #删除log目录下.log的文件

git操作撤销,命令行输入:

1.未add撤销

git checkout xxx

2.add后,未commit撤销

git reset HEAD xxx
git reset HEAD .  #撤销当前所有修改

3.commit后撤销

git reset --soft HEAD^    #撤销commit回到前一个版本,不撤销git add,不恢复变更
git reset --soft HEAD~1  #撤销commit回到前一个版本,不撤销git add,不恢复变更
git reset --soft HEAD~2  #撤销commit回到前两个版本,不撤销git add,不恢复变更
git reset --mixed HEAD^ #撤销commit回到前一个版本,并撤销git add,不恢复变更
git reset --hard HEAD^   #撤销commit回到前一个版本,撤销git add,恢复变更

4.恢复到任意一次提交

git reset --hard commitid

查看提交历史,命令行输入:

git log

变更隐藏和回复,命令行输入:

git stash save "save message" 将变更暂时存储并隐藏,不包含未被追踪的文件
git stash list  stash列表
git stash show 显示做了哪些改动,默认show第一个存储,如果要显示其他存储,后面加stash@{$num},比如第二个 git stash show stash@{1}
git stash show -p 显示第一个存储的改动,如果想显示其他存存储使用git stash show  stash@{$num}  -p ,比如第二个:git stash show  stash@{1}  -p
git stash apply 恢复某个存储,但不会把存储从存储列表中删除,默认使用第一个存储,即stash@{0},如果要恢复其它,git stash apply stash@{$num} ,比如第二个:git stash apply stash@{1}
git stash pop 恢复之前存储的变更,将缓存堆栈中的对应stash删除,并将对应修改应用到当前的工作目录下,默认为第一个stash,即stash@{0},如果要应用并删除其他stash,使用git stash pop stash@{$num} ,比如应用并删除第二个:git stash pop stash@{1}
git stash drop stash@{$num} 丢弃stash@{$num}存储,从列表中删除这个存储
git stash clear 删除所有缓存的stash
git stash push -u 将变更暂时存储并隐藏,包含未被追踪的文件
git stash push -a 被修改的文件连同被忽略文件也被存储起来

常规操作的命令可以用下面一张图来演示。 EzyqMv2.jpg!web

推荐阅读:

String引发的提问,我差点跪了

就写了一行代码,被问了这么多问题

面试官:JVM对锁进行了优化,都优化了啥?

synchronized连环问

如有收获,请点击底部右下角" 在看 ",谢谢!

MfUNZvm.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK