目录
github使用说明
前言
什么是github,github能做什么?它是一个代码仓库,但是你可以把它当做一个远程硬盘(云盘、网盘)。和一般网盘的区别在于,它通过一个叫做git的软件进行同步,上传或下载操作,这个软件有非常强大的同步功能,甚至可以建立不同的存储分支,让同步的文件有多个不同的版本(这对代码开发来说是很有意义的)。但是,如果你不想要,不需要这个功能,也可以把它当做纯粹的免费网盘使用。
现在就有很多人,通过github来存放个人笔记,日记,博客等文档资料,在不同电脑上,只要同步一下,就能继续编辑,非常方便。这个甚至比一般的网盘要来得轻松,因为git对版本的控制更好,数据传输量更少。同步更加智能化,版本管理更加方便。
多说无益,本文介绍git的原理和使用方法,你亲自看一下是否对你有用。
一、git原理
1.1 分布式储存
git是一个分布式同步工具,简单来说,它不要求你建立一个特定的服务器,你可以在任何地方建立自己的存储仓库,不同的仓库没有地位的差异,都是平等的,这就去中心化了,所以叫分布式。
这有什么好处,比如你不需要依赖某个网盘提供商,如果它不小心倒闭了,只要你把仓库搬到另一个提供商就可以,甚至,你直接弄一台电脑放仓库也是可以的。这个理念我认为是非常有居安思危精神的。千万别过于相信网络,要给数据一个备份。
如果是传统的网盘,你需要用它的软件,下载文件到你的电脑,然后用另一家的软件,上传到另一个网盘备份,不用两天你就受不了。如果你不跟踪备份,可能网盘没通知你就把你的学习资料删除了,那你是怎么也要不回来的。相信经历过的朋友都知道。而只需用git,就能统一的管理各个仓库,轻松进行复杂的同步。
1.2 版本库 repository
也就是储存仓库,但是这个仓库不但可以放文件,还对文件的修改、删除等历史记录作跟踪,让你可以回溯到某个历史时期。这是git 最强大的功能。
为了利用好版本管理功能,要求文件类型是文本类型,最好是UTF-8编码的。
git init #在当前目录下建立仓库,将新建一个.git文件夹来存储仓库数据
1.3 添加到仓库
在当前目录新建文件,然后加入仓库,git即可维护该文件的同步管理。
git add file.txt #将当file.txt文件加入git仓库
1.4 提交到仓库
添加并不会立刻提交,也不会自动同步,这个交给用户自己判断恰当的提交时机。
git commit -m "这里写备注" #提交数据
每次提交,都要求你写一个备注,来记录你这次提交做了什么,这是个良好的习惯,因为这样你回溯历史的时候,才知道哪个版本是你的目标版本。
1.4.1 显示文件变化
当前被跟踪的文件,和仓库版本有什么差异,可以用git status
列出哪些文件改动了的文件。
如果你还想知道具体修改了什么地方,可以用git diff xxx
来查看xxx文件的改动情况。
1.5 变更到不同的历史版本
这个是比较高级的功能,也许你用得上,也许用不上。首先,git reflog
会显示你提交文档的版本:
#样例输出f9adcba (HEAD -> master) HEAD@{0}: reset: moving to f9adc9a75fd8 HEAD@{1}: reset: moving to HEAD~1f9adcba (HEAD -> master) HEAD@{2}: commit: 测试回退功能9a75fd8 HEAD@{3}: commit: 加了一点781b0ad HEAD@{4}: commit: 有加了一点55227c1 HEAD@{5}: commit: git文档修改f2d5850 HEAD@{6}: commit (amend): ss2ff6877d HEAD@{7}: commit (initial): ss
要跳到那个版本,使用git reset --hard f9ad
,其中f9ad
用具体的版本号替换。在以上样例的第一列,只需要给出几位,git就会寻找到对应版本。
当跳转到该版本,相应的文档数据会立即修改。
1.6 工作区和版本库的区别
当你在某个目录下面,创建仓库git init
,该目录就是你的工作区,而具体的仓库的一些配置信息,是放在.git
目录下的(可能被隐藏)。
工作区的作用就是你要同步的文件,应该放在这个目录下面,你只需要修改对应的文件,git负责其余的一切。你不需要刻意的去修改或者理会.git目录下面的内容,而应该使用git软件来输入命令。
1.7 暂存区 stage
当用git add xxx
添加文件,实际上就是把文件放入暂存区。当git commit
就会把暂存区的内容正式提交到某个分支(以后解释分支),并清空暂存区。
如果要撤销工作区的修改,可以用git checkout -- xxx
;如果要撤销暂存区的修改,可以用git reset HEAD xxx
。
1.8 远程仓库
还记的我说过git是分布式的么?如果只是在自己电脑目录创建一个仓库,那就没什么意思了,现在学习怎么添加远端的仓库。
首先,申请github.com网站的账户,并申请一个仓库的使用权,然后得到一个仓库的链接地址,然后:
git remote add 仓库别名 链接 #添加远端仓库git push -u 仓库别名 master #将本地分支master提交到仓库。-u表示关联对应分支git remote -v #显示远程仓库情况git pull #从远程仓库拉取最新版本
1.9 分支
git允许建立不同分支,所谓的分支,就是另一条修改文件的路线。git记录每一条路线不同的修改内容,当合并的时候会要求你对有冲突的修改内容进行选择。
git branch git #创建名为git的分支git checkout git #切换到git分支git branch #查看当前分支情况git merge git #将git合并到当前分支git branch -d git #删除git分支
创建分支后,所做任何修改,只影响当前分支,当切换分支,文件会瞬间回到另一个分支的状态下。
虽然git分支这个功能看上去很强大,又很复杂,但实际上运作非常高效,它总能按照你期望的方式去运作。因此,推荐多使用,用来划分不同的关注点也是挺好的。
一个合理的使用分支的方法是:
- 保持主分支不变,其他分支修改
- 然后将当前主分支合并到某个分支,解决冲突
- 最后用主分支合并该分支,更新主分区
这样就算再多人一起合作编辑,也能保证主分支是稳定可靠的。
不过,最好还是不要随便跳转分支,因为跳转分支容易导致当前数据丢失,然后还会导致很多很多的误操作。(本博客就是这样丢失了一些历史信息,╮(╯▽╰)╭!)q
1.10 储藏
特别要注意,切换分支是一个高度危险的操作!因为它会毁掉当前工作区的状态。切换分支之前,应该先提交当前修改内容到仓库。但是,如果占时无法提交,可以借助储藏功能,把当前工作区保存在一个栈表里面。
git stash #保存并清空当前工作区修改状态git stash pop #回弹当前工作区上一个状态。git stash list #显示储藏栈表
1.11 标签
标签对应了commit 提交版本,因为这个版本号太复杂,因此用标签名来代替,更加容易识别。也作为一个发行版本号来使用。
git tag v0.9 #当前版本命名为标签v0.9git tag #查看当前标签git tag -d v0.9 #删除对应标签git push origin v0.9 #提交标签到远程仓库
二、实践
2.1 流程总结
纵观理论知识,我们可以得出一些结论:
- git是分布式,多仓库的,其中最直接的仓库就在当前工作目录下的.git文件,还能添加远程仓库。
- 工作流程是: 初始化并配置git本地仓库--》在工作区编辑文件--》提交到暂存区--》本地仓库--》远程仓库。
- 有回退的机制:远程仓库——pull或clone—》本地仓库--版本回溯--》当前版本工作区。
- 有储藏现场机制: 工作区、暂存区--stash--》储藏堆栈; 储藏堆栈--stash pop--》恢复当前工作区、暂存区。
- 有版本控制: 每次commit提交,就会产生一个版本记录,任何时候,只要有这个记录就能回退到该版本的状态。因此,commit之后的操作至少可以保证该版本的数据是安全的。
- 有分支能力: 分支对团队来说是最重要的支持能力,恰当使用分支,可以多人同时编辑,而不会产生太多数据冲突。
有些操作是危险的:如果没有提交到仓库,就切换分支,当前工作内容就可能丢失,因为切换分支并不懂得自动保存当前工作区的状态。只要是会对当前工作区产生影响的操作,都有可能影响到数据安全(如果当前工作区没有提交,有修改状态),比如拉取,同步,切换分支,版本跳转,因此这些操作之前特别要注意查看当前工作区状态,先提交,或者储藏现场。
工作区和暂存区,都是不安全的,可以视为临时数据,他们之间形成一个小型的临时版本控制机制。工作区的内容可以放到暂存区,暂存区的内容也可能回退到工作区。
2.2 配置git
2.2.1 安装git
git 官网在,下面以deepin系统为例:
apt install git #安装gitgit --version #版本号2.17
2.2.2 创建本地仓库
cd ~mkdir gittestcd gittestgit init #在gittest目录下创建仓库,当前工作区即是gittestgit help #查看使用说明
2.2.3 克隆远程仓库
非新项目,可以从远程仓库克隆创建。
git clone https://github.com/Anduin2017/HSharp.git cloneProj #从已有的远程仓库克隆创建名为cloneProj的本地仓库。该远程链接可以在github任意一个公开项目中获取。
2.2.4 配置本地仓库信息
git仓库的配置信息,可以存在三个地方:
- system 影响所有用户,文件位于
/etc/gitconfig
- global 影响当前用户,
~/.gitconfig
- local 影响当前仓库,
.git/config
#添加用户名和邮箱信息,必备,因为每一次提交仓库都要记录当前提交人的这些信息git config --local user.name "myname" #添加user.name的记录为myname,其中--local为默认值,可以省略git config user.email myname@163.com #添加user.email为myname@163.comgit config --list #列出当前已经配置的内容git config user.name #列出某项的值,即myname
2.2.5 忽略某些文件 .gitignore
如果不想某些文件被加入仓库,可以用.gitignore文件指定,内容例如:
#忽略.cpp文件和~符号结尾的文件*.cpp*~
2.3 检查工作区和暂存区状态
应该经常使用这个命令确认文件状态。
注意: 如果你使用编辑器来编辑,要注意当前编辑状态和当前文件状态是不同的。git是针对文件而不能知道你有没有正在编辑。如果你正在编辑,又通过git命令修改了文件,你的编辑器就会提示你原始文件已经被修改,你无法保存等异常状态。因此,你在使用git命令前,应该先把编辑状态下的文件,用编辑器保存一下。
git status #查询状态
2.4 正式进入工作
2.4.1 工作区和暂存区交互
- 编辑工作区上的文件,然后放到暂存区,继续编辑
- 如果编辑后不满意,用暂存区的内容恢复到工作区
- 如果满意,继续放进暂存区
#创建一个新文件,并在末尾输入内容“hello world!”cat >>myfile.txthello world!^C#将该文件放入暂存区git add myfile.txt#继续编辑,并在末尾添加“hello.”cat >>myfile.txthello.^C# 这时,就存在两个不同的版本,一个是修改前放进暂存区的"hello word!"版本,一个是工作区内的“hello word!hello."版本。git status #遇事不决,先用这个确认状态。可以发现同一个文件,有两种状态。一种是放在暂存区的,提示让你提交;另一种是在工作区的,提示让你暂存变更。git diff myfile.txt #确认一下文件内容的差别。显示工作区和暂存区两个同名文件的对比,如果你会看diff输出格式,就能发现差别是添加"hello."#选择1,放弃当前修改,返回上次暂存内容git checkout -- myfile.txt#选择2,确认修改,放入暂存git add myfile.txt#git status的提示很丰富,提示上面有以上指令的使用格式#删除文件#如果某个文件被删除,这在git中也是一个特殊的修改git rm xxx #删除xxx,并把删除情况告知暂存区git rm --staged yyy #从暂存区删除yyy文件,但不影响工作区
2.4.2 暂存区和本地仓库交互
暂存区的内容并不会产生一个稳定的版本,最终要正式加入仓库,需要commit提交。
提交会要让你输入每次提交的备注,命令行下面的编辑器由git config core.editor
选项控制。
git commit #向本地仓库提交暂存区的内容git reset HEAD myfile.txt #取消暂存区内容。git diff --staged #比较暂存区和仓库版本的不同git commit -a -m ”备注“ #把工作区的改动全部暂存起来,然后直接提交。-a参数将缩减提交的步骤。git log -2 #观察最近两条提交记录git log --pretty="%h %an,%ar:%s" #按指定格式显示记录git log --pretty=oneline --graph #显示按分支提交结构图apt install gitk #安装图形化显示记录的工具
2.4.3 远程仓库
git remote -v #查看远程仓库列表git remote add remotegit https://github.com/Anduin2017/HSharp.git #添加一个远程仓库,命名为remotegitgit fetch remotegit #下载远程仓库remotegit的对象和引用git remote show remotegit #显示远程仓库关联信息git remote rename remotegit newname #把远程仓库命名进行修改git remote rm newname #删除远程仓库的关联git clone https://github.com/Anduin2017/HSharp.git clonegit #克隆生成一个完全一样的本地仓库,创建工作目录为clonegit
2.4.3.1 远程分支
远程仓库上面的分支,称为远程分支。
git remote -v #查看远端仓库情况git remote add gitbak file:///home/user/git #加入本地另一处仓库git fetch gitbak #下载仓库信息git branch -av #查看所有分支,包括远端仓库git branch --set-upstream-to=gitbak/master #当前分支关联gitbak/master分支git pull gitbak master --allow-unrelated-histories #合并不关联的历史数据git push cloneProj HEAD:master #提交本地分支到远端仓库#注意:应该尽量保持本地分支名称和远程一致。
2.4.4 储藏
这个功能比较简单。
git stash #储藏当前工作区和暂存区变动git stash pop #恢复
2.4.5 版本
每一次提交,都会产生一个版本,任何时候都可以跳转到该版本,但是版本标识是一个字符串哈希序列(sha-1 40字符哈希),不方便对外公布。
git tag #显示现有标签git tag v1.0 #为当前版本命名标签git show #显示HEAD关联提交对象信息git show v1.0 #显示特定版本信息git push origin v1.0 #向远程仓库提交标签git commit -am"备注" #创建一个新提交"commit"对象,并改变HEAD指针指向新对象,如果头指针指向某个分支,先改变该分支指向新对象,再用HEAD指针指向该分支。简而言之,创建新版本git reset 2341a --hard #HEAD指针调到散列为2341a开始的commit对象,即跳到该版本,--hard参数重置当先工作区和暂存区
2.4.6 分支
git中的分支,类似一个指针,一个提交(commit)类似链表中的一个节点。commit保存了父节点,和整个目录树结构,因此当分支(指针)指向某个commit,内容自然就是该节点的状态。理解这点很重要。也许你会担心每次提交都产生一个当前文件目录的快照,会不会导致存储量变得很大,实质上是不需要担心的。git以一种高效的方式进行相关记录,不管是性能还是空间,都允许你瞬间执行提交,产生新的快照。
一开始有个master的分支,有个HEAD指针,它指向master,而master指向具体的commit节点,每次提交,master都会更新到新的节点上。如果你使用某种命令移动这个指向,自然就会变更到不同的版本,这就是实现版本管理的基础。
git branch -va #显示分支情况,-a表示所有分支,包括远程分支,这个命令经常使用,因为它会显示分支的情况,类似git status显示文件情况。git branch testbranch #基于当前分支创建一个新分支,实质不过是复制一个指针而已,效率非常高#当你想切换到其他分支#1. 保存正在编辑的文件#2. 储藏现场git stash push -m "备注"#2.2 或者提交git commit#3. 切换分支#为什么?因为切换分支会清空现场,你将丢失掉工作区和暂存区的改动数据git checkout testbranch #切换到testbranch,它和此前分支上一次提交版本一模一样git commit -m “备注” #每次提交,都会移动当前分支,指向最新的commit对象#分支的成果最终要合并起来git merge master #将master分支合并到当前分支#合并过程可能会出现数据冲突,参考提示,手工选择后合并成功git checkout mastergit merge testbranch #更新master分支,一般这个分支作为默认的主分支git branch -d testbranch #删除分支#如果该分支新增的内容没有被合并到其他分支,就会提示让你先合并,善用git status,根据提示来操作即可。git mergetool #调用图形界面来解决内容冲突
分支的功能并不算太复杂,但是如何设计适合自己需要的分支系统,就是一门值得思考的功课。建议master作为集成主分支,有若干分支负责实际编辑,编辑完毕合并到主分支。然后每个细分支有里程碑(用标签标注),发布版本应该选择恰当的里程碑组合,进度稍微比主分支慢。
另外,如果需要修补,可以选择在里程碑新建一个补丁分支,它需要和主分支合并,也需要和发布分支合并。
- master分支,负责最新版本的持续集成
- publish分支,负责里程碑版本的特定集成
- featureX分支,负责各项具体内容,并在特定版本打下里程碑标签
- patch分支,负责修补漏洞,选择要修复的版本,并打上修订号。因为集成是一件比较费劲的事情,所以可以选择只对特定版本进行修复。
可能用到的技术包括:
- 创建分支:
git branch 分支名 提交对象
- 跳转到该分支:
git checkout 分支名
- 编辑,保存,提交
- 跳转到集成分支:
git checkout xxx
- 集成:
git merge x1
- 打标签:
git tag v1.0.0.f1
- 分离头指针:
git checkout --detach
- 跳转版本:
git reset --hard commit对象
集成merge的基本逻辑是:
- 寻找共同祖先,计算出各自不同点
- 让用户选择合并那些内容
因此应该采取的策略:每个分支应该尽量寻求共同祖先。那么共同祖先是怎么来的?实质以前某个merge节点(或者是分裂的初始点),否则分支怎么会有共同祖先?因此解决集成merge冲突复杂化的办法,实际是分成多次集成,也就是持续集成的意义所在。
其他使用技巧
- 如果显示的中文文件名变成了
\344\275\277\347\224\250
类似的编码格式,可以用:git config --global core.quotepath false