Git是什么
Git能解决什么问题
Git能解决什么问题?答曰:版本控制。
经常写文档的同学应该比较清楚,对某个文档修改了一点以后,又不想直接覆盖,这样的话,后面发现写错了,就恢复不回来。所以就复制出很多文件名不一样,但是内容差不多的文件。
这样也不是不可以,但是问题在于,Copy了很多份,太消耗空间。同时查找恢复也并不方便。
所以我们希望有这样一种软件,它可以
- 自动记录文件的改动
- 可以团队协作编辑
比如这样
版本 | 文件名 | 用户 | 说明 | 日期 |
---|---|---|---|---|
1 | service.doc | 张三 | 删除了软件服务条款5 | 7/12 10:38 |
2 | service.doc | 张三 | 增加了License人数限制 | 7/12 18:09 |
3 | service.doc | 李四 | 财务部门调整了合同金额 | 7/13 9:51 |
4 | service.doc | 张三 | 延长了免费升级周期 | 7/14 15:17 |
其实仅仅是对Word文档进行版本控制,我觉得有道云协作就可以了,但是它需要使用外网,而且不是利用Windows自带的目录,感觉还是不太方便。
分布式版本控制平台
其实版本控制器还有很多,比如CVS和SVN,但是它们都是集中式的控制系统。
所谓集中式,自然有个Master级别的角色,它可以保存所有的版本库。大家需要先从版本库里面获得最新的版本,修改以后再上传。这样Master自然就有了所有分支最新的版本了。
缺点是必须联网,是不是很类似与上面说到的有道云笔记啊!
那分布式版本控制系统有啥区别呢?分布式系统没有Master这个角色,所有的终端一视同仁,每个人都有一个完整的版本库。那怎么协作呢?只需要互相通信,互相推送就可以了。
分布式系统的优点在于安全,一个人的电脑坏了,还有其他人的电脑作为备份嘛。
当然在实际应用的时候,一般不会有两个人互相推送,还是会引入一个中央服务器,但是它就类似于一个交换机,只是用来交互数据,没有它大家也可以在本地干活。
而且Git还有强大的分支管理功能,还是免费的。现在最快、最简单也最流行的就是Git了。
安装Git
- Linux上安装
sudo apt-get install git
- Windows上安装
参考【使用教程】CMDer,Window下CMD的替代者
安装Cmder即可,里面就自带了git功能。
配置
首先要进行全局设置:
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
提交版本
创建版本库
什么是版本库?就是仓库,respository,可以理解为一个目录,里面的所有文件可以被Git管理起来,里面的文件修改、删除都跟跟踪到。
下面来创建版本库,
在需要创建版本库的地方里面打开cmder,输入
git init
当前目录下多了一个.git的目录,这个目录是Git来跟踪管理版本库的
某些文件不提交
有些时候需要把某些文件放到目录中,但是又不能提交它。可以在Git工作区建立一个.gitignore
文件,里面填充要忽略的文件名
我们可以通过.gitignore网站查看各种配置文件,组合一下即可用。
一般需要忽略那些文件呢?
- 操作系统自动生成的文件或者编译生成的文件。
- 忽略敏感信息,比如密码
比如Python里面,
# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini
# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build
# My configurations:
db.ini
deploy_key_rsa
有的时候,想添加一个文件到Git,发现添加不了,多半是被忽略了。可以使用
git add -f <文件>
也可以看是哪条规则限制了这个文件的上传
git check-ignore -v <文件>
将文件提交到版本库里面
所有的版本控制系统都只能跟踪文本文件的改动,比如TXT文件、代码等。
对于图片、视频、Word等都是二进制文件,虽然仍然可以由版本控制系统管理,但是没有办法对比文件系统的变化。也就是说通过Cmder无法对比两个版本之间的差别。
所以使用Git主要还是针对于代码文件、TXT文件等进行版本控制,需要注意的是
使用windows进行编码的时候,建议使用Notepad++将默认编码设置为UTF-8 without BOM
添加文件到Git仓库,分两步:
第一步,使用命令
git add .
,注意,可反复多次使用,添加多个文件;
要随时掌握工作区的状态,使用git status
命令。
如果git status告诉你有文件被修改过,用git diff readme.txt
可以查看修改内容。git diff HEAD -- readme.txt
可以看查看工作区和版本库里面最新版本的区别第二步,使用命令
git commit -m "备注"
进行正式提交。
实际上每次执行git commit -m
就保存了一次快照
,类似于打游戏的时候存一次档。如果我们想回退的话,可以通过快照来进行恢复rollback
可以使用git log --pretty=oneline
命令显示从最近到最远的提交日志,以时间轴的形式显示日志提交。
版本回退
上面一章我们讲了,可以使用git commit
进行提交,然后使用git log --pretty=oneline
查看有提交的版本。
$ git log --pretty=oneline
3628164fb26d48395383f8f31179f24e0882e1e0 append GPL
ea34578d5496d7dd233c827ed32a8cd576c5ee85 add distributed
cb926e7ea50ad11b8f9e909c05226233bf755030 wrote a readme file
其中类似3628164...882e1e0的是commit id(版本号),为了保证多人提交的环境下,commit id不同,所以Git使用了SHA1计算出来的一个非常大的数字,用十六进制表示,这样就可以避免冲突了。
Git就会把每个版本自动串成一条时间线
如何进行版本回退
首先,Git必须知道当前版本是哪个版本,
- 用
HEAD
表示当前版本, - 上一个版本就是
HEAD^
- 上上一个版本就是
HEAD^^
, - 当然往上100个版本写成
HEAD~100
然后使用git reset
开始回退
git reset --hard HEAD^
如果发现回退错了,所以想回到最开始的版本。
- 如果命令行窗口还没有被关掉,可以顺着往上找直到找到那个版本的ID是3628164...,于是就可以指定回到未来的某个版本:
$ git reset --hard 3628164
- 如果命令行窗口关闭了,可以使用
git reflog
来查看执行commit命令时候的commit id。然后使用git reset
总结一下:
现在总结一下:
HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令
git reset --hard commit_id
。穿梭前,用
git log
可以查看提交历史,以便确定要回退到哪个版本。要重返未来,用
git reflog
查看命令历史,以便确定要回到未来的哪个版本。
几个概念
首先解释几个名词:
- 工作区:指的是建立了git的目录,也就是平时我们进行代码编辑的地方
- 版本库:工作区有一个隐藏目录.git,是Git的版本库。
版本库里面有暂存区(Stage)
、分支(Master)以及指向分支的指针HEAD
其中Git区别于其他的版本控制系统的一个不同之处就是有了暂存区
git add
命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行git commit
就可以一次性把暂存区的所有修改提交到分支。
理解了这个,我们来可如下的过程
第一次修改 -> git add -> 第二次修改 -> git commit
可以发现第二次修改以后并没有git add
,也就是第二次修改的内容没有放到暂存区,所以git commit不会把第第二次的修改提交了。
这就是Git比其他版本控制系统优秀的地方,因为Git跟踪管理的是修改,而不是文件
没有提交到分支之前的撤销
之前我们说到了如果已经git commit
到分支以后,要进行版本回退应该怎么做。
但是如果我们只是添加到了暂存区,甚至还没提交到暂存区,此时应该如何撤销呢?
- 只是修改,没有add到暂存区:
git checkout -- file
使用这个命令还可以把误删的文件恢复回来
- 已经add到暂存区,但是没有commit
- 首先使用
git reset HEAD file
回退到工作区。 - 然后使用
git checkout -- file
把工作区的修改撤销了。
- 首先使用
总之,记住一点:git checkout -- 文件
命令,撤销的是工作中文件的修改,而git reset HEAD -- 文件
命令,撤销的是暂存区中文件的修改。
撤销本地所有修改
git checkout . #本地所有修改的。没有的提交的,都返回到原来的状态
git stash #把所有没有提交的修改暂存到stash里面。可用git stash pop回复。
git reset --hard HEAD #返回到某个节点,不保留修改。
分支
分支有什么用?每个人可以创建自己的分支,想提交就提交,直到完成所有的功能以后,一次性合并到原来的分支上,这样可以不影响别人工作。
Git好就好在切换分支只需要不到1s,比SVN等快很多。
创建分支
Git默认有一条主分支,即master
分支,而HEAD
指针实际上指向当前分支的,此时就是master
当创建新的分支后,Git新建了一个指针dev
,HEAD
指向dev
,切换到了dev
上了。然后后面的修改都是在dev
上了,master
指针不变。
当我们在dev
上把工作完成了,就可以合并分支。方法就是直接把master
指向dev
当前的提交。
下面是具体用命令怎么做
- 创建dev分支并切换
$ git branch dev
$ git checkout dev#切换分支
- 正常提交
- 切换到master分支上
- 合并指定分支dev到当前
master
分支
git merge dev
此时有可能两个分支都有提交,所以无法自动合并,需要手动解决冲突,再提交
- 删除分支
$ git branch -d dev
分支策略
在实际团队协作的时候,应该保证master
非常稳定,只 是用来发布新的版本,平台应该不在上面修改。
每个人可以经常生成一些dev分支, 在上面进行修改,当有必要的时候,再合并到主分支上即可。
场景一:Bug分支
每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
当你接到一个修复一个代号101的bug的任务时,可以创建一个分支issue-101来修复它,但是dev上的工作只进行到一半,还没法提交,怎么办?
- 可以使用stash功能把现场存储起来,之后可以恢复
git stash
- 然后创建Bug分支,进行修复
- 切换到master上,合并,删除分支
- 现在应该回到dev分支继续干活了。恢复现场
git stash apply
:恢复以后stash内容不删除,需要再使用git stash drop
git stash pop
:恢复的时候同时把stash的内容也删除了。
因为可能多次保存现场,所以可以先使用git stash list
查看,然后恢复指定的stash
$ git stash apply stash@{0}
场景二:Feature分支
开发一个新feature,最好新建一个分支,但是还没开发完的时候,收到通知取消此特性。
此时当然直接删除就好,不过问题就在于分支还没有被合并,如果删除可以通过git branch -D <name>
强行删除。
使用标签
发布一个版本的时候,可以先在版本库里面打一个tag。标签其实就是版本库的一个快照
既然有commit,为什么还要tag呢?
比如要将上周一的版本打包发布,我们知道commit id是一堆乱七八糟的数字,并不好查找,如果加上一个tag v1.2
,就有了实际意义,可以与某个commit绑在一起,更好查找。
如何打标签
- 首先,切换到打标签的分支上
git branch
- 使用
git tag v1.l0
创建一个标签,使用git tag
可以查看所有标签,标签不是按照时间排序,而是按照字母排序 - 如果要绑定历史的 commit,可以怎能先使用
git log
找到commit id
git tag v0.9 commit id
- 可以创建带有说明的标签
git tag -a v0.1 -m "version 0.1 released" 32321332
可以使用 git show v0.1
查看说明。
如果标签打错了,能改吗?当然能。删除即可。
$ git tag -d v0.1
因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
远程仓库
到现在为止我们已经学会了如何在自己的电脑上进行工作,本章则将讲解如何把代码托管到Gitee远程仓库来进行管理,这样就可以进行协作以及代码的备份呢。
之前我们只是在自己的电脑上搭一个Git仓库,实际上也可以分布到不同的机器上,别的机器只要复制原始版本就好了,这样就可以保证大家都一样。
所以完全可以搭一个Git服务器,然后所有的人都从服务器里面复制一份到自己这边,再把各自的提交推送到仓库里面,实现协作。
最著名的Git服务器当属GitHub
,不过在国内比较的慢,所以我们使用中国版的GitHub
——码云(gitee.com)
上传公钥
本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以需要先上传公钥,想知道原理可以点击HTTPS
- 首先创建SSH Key
打开CMDer,输入
ssh-keygen -t rsa -C "youremail@example.com"#需要把邮件地址换成你自己的邮件地址
然后一路回车
可以通过everything
搜索一下id_rsa.pub
这个公钥在那里,然后使用Notepad++打开,复制所有的内容。
- 在Gitee的设置里面粘贴公钥的内容。
为什么要SSH Key呢?
因为码云要识别这个推送确实是你本人干的。
当然码云支持多个Key,如果有多个电脑,可以建立多个Key
将代码推送到远端
如果要把代码推送到远端,有两种场景,一是现在已经在本地建立了仓库了,现在想在Gitee上同样建立一个,然后合并即可。
另一种是现在啥都没干,直接从Gitee上建一个,然后clone到本地即可。
首先在Gitee上建立一个新仓库名字是articlespider
- 如果已经在本地建立了仓库
- 首先关联远程库:
git remote add origin git@server-name:path/repo-name.git
- 使用命令
git push -u origin master
第一次推送master分支的所有内容; - 此后的提交只需要使用
git push origin master
- 首先关联远程库:
cd existing_git_repo
git remote add origin https://gitee.com/***/articlespider.git
git push -u origin master
如果在新建远程仓库的时候加上了README.MD
,但是这个Readme.md
又不在本地库里面,所以会报错。可以
$ git pull --rebase origin master
当然也可以在本地根目录新建一个Readme.md
- 如果现在还没有建立仓库,可以使用
git clone
复制一个版本下来。
git clone git@gitee.com:****/articlespider.git
多人协作
创建了远程仓库以后,可以进行多人协作。可以将本地的分支推送到远端,也可以从远端拉取
推送分支
远程仓库默认名称是origin
可以使用git push进行推送。
git push origin 分支
拉取分支
现在另一个人需要在dev分支上做开发,首先应该先clone一份到本地。
git clone git@gitee.com:****/articlespider.git
当从远程仓库克隆的时候,Git自动把master与远程的master对应起来。
查看远程库的信息:git remote -v
此时只能看到master
分支,如果也要在分支dev
上开发的话,必须创建远程origin的dev分支到本地,
git checkout -b dev origin/dev
然后就可以继续开发了。
那么我们怎么与之协作呢?
- 首先可以使用
git push origin <branch>
推送自己的修改 - 如果推送失败,则远程分支比本地的更新,需要先用
git pull
合并。
如果提示no tracking information
,说明链接关系没有建立起来。使用
git branch --set-upstream <branch> origin/<branch>
- 如果合并有冲突,则解决冲突,并在本地提交
- 冲突解决之后,再用
git push origin <branch>
推送标签
因为创建的标签都只存储在本地,不会自动推送到远程
如果要推送某个标签到远程,使用命令git push origin <tagname>
或者,一次性推送全部尚未推送到远程的本地标签:
$ git push origin --tags
如果要删除的话,首先需要先删除本地的。
$ git tag -d v0.9
然后,从远程删除。
$ git push origin :refs/tags/v0.9
修改远程库的名字
我们可能同时进行多个项目,他们都需要推送到远端。比如learngit项目,现在要与远程库关联
git remote add origin git@gitee.com:<your name>/learngit.git
如果报错fatal: remote origin already exists.
说明本地库已经关联了一个名叫origin的远程库
可以先删除
git remote rm origin
再关联一个远程库gitee
git remote add gitee git@gitee.com:<gitee name>/learngit.git
此时远程库的名称叫gitee,不叫origin。
如果要推送:
git push gitee master
配置别名
所谓配置别名其实就是配置命令的简写,比如使用git st表示git status等。
$ git config --global alias.st status#查看工作区状态
$ git config --global alias.co checkout#切换分支,撤销修改
$ git config --global alias.ci commit#提交
$ git config --global alias.br branch#分支
$ git config --global alias.unstage 'reset HEAD'#回退
$ git config --global alias.last 'log -1'#查看log
$git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
加上--global是针对当前用户起作用的
搭建Git服务器
搭建服务器
需要准备一台运行Ubuntu的机器
- 安装git :
sudo apt-get install git
- 创建git 用户,用来运行git服务:
sudo adduser git
- 创建证书登录
收集所有需要登录的用户的公钥,就是他们自己的id_rsa.pub文件,把所有公钥导入到/home/git/.ssh/authorized_keys文件里,一行一个。 - 初始化git仓库
选定一个目录作为Git仓库,假定是/srv/sample.git,在/srv目录下输入命令:
$ sudo git init --bare sample.git
这样就会创建一个没有工作区的裸仓库,把所有者改为git
sudo chown -R git:git sample.git
- 禁用shell登录
编辑/etc/passwd文件完成。找到类似下面的一行:
git:x:1001:1001:,,,:/home/git:/bin/bash
改为:
git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell
这样,git用户可以正常通过ssh使用git,但无法登录shell,因为我们为git用户指定的git-shell每次一登录就自动退出。
- 克隆远程仓库:
$ git clone git@server:/srv/sample.git
总结
最后把所有的命令总结成一个表格
一级 | 二级 | 命令 |
---|---|---|
提交代码 | 新建仓库 | git init |
提交到暂存区 | git add . | |
提交到分支 | git commit -m "" | |
查看 | 查看状态 | git status |
查看提交记录 | git log --pretty=oneline | |
查看命令历史 | git reflog | |
对比 | git diff HEAD -- readme.txt | |
标签 | git tag | |
远端仓库 | git remote -v | |
版本控制 | 未提交到暂存区 | git checkout . |
已提交到暂存区 | git reset HEAD file | |
已经提交到分支 | git reset --hard <HEAD^> | |
分支 | 创建分支 | git branch dev |
切换分支 | git checkout dev | |
合并 | git merge dev | |
删除分支 | git branch -d dev | |
强行删除 | git branch -D dev | |
存储现场 | git stash | |
恢复现场 | git stash pop | |
标签 | 创建标签 | git tag v0.9 |
删除标签 | git tag -d v0.8 | |
推送远程标签 | git push origin --tags | |
删除远程标签 | git push origin :refs/tags/v0.9 | |
远程仓库 | 上传公钥 | ssh-keygen -t rsa -C "youremail@example.com" |
关联远程库 | git remote add origin https://gitee.com/***/articlespider.git | |
删除远程库 | git remote rm origin | |
第一次提交 | git push -u origin master | |
普通提交 | git push origin | |
拉取 | git pull origin | |
多人协作 | 复制 | git clone git@gitee.com:****/articlespider.git |
创建远端分支到本地 | git checkout -b dev origin/dev | |
创建链接关系 | git branch --set-upstream origin/ | |
别名 | 查看log | $git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue) |
参考
本文主要是根据Git教程整理得到的,目的是帮助Git学习。