前言
发展历史
本地版本控制系统(Version Control Systems,简称VCS)
大多都是采用某种简单的数据库来记录文件的历次更新差异
最流行的:
RCS- 工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。”
集中化版本控制系统(Centralized Version Control Systems,简称 CVCS )
- 诸如CVS、Subversion以及perforce等
- 容易单点故障、丢失所有历史更新记录的风险
分布式版本控制系统(Distributed Version Control System,简称 DVCS)
- 像Git、Mercurial、Bazaar以及Darcs
- 客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录
- 大部分系统以文件变更列表的方式存储信息,这类系统(CVS、Subversion、Perforce、Bazaar等)将他们存储的信息看作是一组基本文件和每个文件随时间逐步累积的差异(**基于差异[delta-based]**的版本控制)
- Git 更像是把数据看作是对小型文件系统的一系列快照。 在 Git 中,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照并保存这个快照的索引。 为了效率,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流
git:分布式版本控制系统,c语言开发
免费集中式版本控制:CVS、SVN — 速度慢,且需要联网
Git
电子书:progit_v2.1.54
简介
git校验和机制:SHA-1散列,这是一个由 40 个十六进制字符(0-9 和 a-f)组成的字符串,基于 Git 中文件的内容或目录结构计算出来
三种状态
- 已提交(committed):表示数据已经安全地保存在本地数据库中
- 已修改(modified):表示修改了文件,但还没保存到数据库中
- 已暂存(staged):表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中

- 工作区:对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改
- 暂存区:是一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。 按照 Git 的术语叫做“索引”,不过一般说法还是叫“暂存区
- git目录:Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据
工作流程
- 在工作区中修改文件。
- 将你想要下次提交的更改选择性地暂存,这样只会将更改的部分添加到暂存区。
- 提交更新,找到暂存区的文件,将快照永久性存储到 Git 目录。
安装
1 | sudo apt-get install git-all |
工具目录结构
- git自带一个
git config的工具来帮助设置,这些变量存储在三个不同的位置
- /etc/gitconfig 文件:包含系统上每一个用户及他们仓库的通用配置。 如果在执行 git config 时带上 –system 选项,那么它就会读写该文件中的配置变量。 (由于它是系统配置文件,因此你需要管理员或超级用户权限来修改它。)
- ~/.gitconfig 或 ~/.config/git/config 文件:只针对当前用户。 你可以传递 –global 选项让 Git 读写此文件,这会对你系统上所有的仓库生效
- 当前使用仓库的 Git 目录中的 config 文件(即 .git/config):针对该仓库。 你可以传递 –local 选项让 Git 强制读写此文件,虽然默认情况下用的就是它。。 (当然,你需要进入某个 Git 仓库中才能让该选项生效。)
- 每一个级别会覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量
1 | git config --list --show-origin # 查看所有配置以及他们所在文件 |
- git工作目录下每一个文件只有两种状态
- 已跟踪:指那些被纳入了版本控制的文件,在上一次快照中有它们的记录。即git已经知道的文件
- 未跟踪:其他文件
操作
版本库(Repository)
工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。

- 创建版本库
1 | git init |
- 到需要版本管理的目录下
- 初始化后会在目录下形成一个
.git文件,此文件为隐藏文件,需要使用命令ls -a查看
- 使用步骤
1 | git add file |
- 添加本地修改后的文件到暂存区
1 | git status |
- 时刻掌握仓库当前情况,查看未被提交的修改,包括工作区和暂存区
1 | git commit -m '注释' |
- 将暂存区的修改提交到版本库中
1 | git commit -m '注释' |
- 第一次提交后发现漏了几个文件,可以使用
--amend添加,此次提交将会替代上一次的提交,上一次的提交将不会出现在版本历史中ß
- 版本管理
1 | git log |
- 显示从最近到最远的提交日志
- format格式设置:
1 | git last |
- 查看最后一次提交
取消暂存 reset
1 | git reset --hard HEAD^ |
- 回退到前一个版本
HEAD:相当于一个指针,指向master,而master指向提交^:是上一个版本,^^:前两个的版本,HEAD~100:往上100个版本
1 | git reset --hard commitId |
- 回退到版本号为
commitId的版本 - 注意:
--hard是一个危险的选项
1 | git rerset HEAD <file> |
- 撤销暂存区(unstage)的修改回退到工作区
1 | git reflog |
- 记录了你的每一次命令,可以用来找已经撤销掉的版本id
- 其他操作
1 | git diff file |
- 查看修改内容,
diff均是未提交的内容
1 | git diff --staged |
撤销操作 checkout
1 | git checkout -- <file> |
- 撤销上一次修改,回到最近一次git commit或者git add状态,不管是在工作区还是在暂存区
- 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令
git checkout -- file。 - 场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令
git reset HEAD <file>,就回到了场景1,第二步按场景1操作。 - 场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
- 场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令
删除文件 rm
1 | rm file |
- 通过rm删除了文件之后,status会告诉删除信息,因为删除也算是修改操作(deleted: file)
- 在本地目录中删除文件之后可以选择彻底删除或者使用git恢复
- 彻底删除:
$ git rm file— 在暂存区中删除,$ git commit— 确定要在版本库中删除- (如果一个文件已经被提交到版本库,那么你永远不用担心误删,但是要小心,你只能恢复文件到最新版本,你会丢失最近一次提交后你修改的内容。)
- 恢复:
$ git checkout --file- 其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
- 彻底删除:
改名操作 mv
1 | git mv old new |
忽略文件
一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件的模式
1 | cat .gitignore |
格式规范:
- 所有空行或者以 # 开头的行都会被 Git 忽略。
- 可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
- 匹配模式可以以(/)开头防止递归。
- 匹配模式可以以(/)结尾指定目录。
- 要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。
远程仓库(github)
- 创建电脑用户自己的密钥
1 | ssh-keygen -t rsa -C "youremail@example.com" |
- 在用户主目录
/home/username下创建密钥 - 生成
id_rsa和id_rsa.pub
- 绑定电脑用户和github用户
- 将公钥
id_rsa.pub加载到github网站上Add SSH Key
- 添加远程仓库
1 | git remote add origin git@github-link |
- 链接本地仓库和github上创建的远程仓库
origin为远程仓库的默认名字,可自行更改
1 | git push |
- 将本地仓库的内容推送到远程仓库上,实际上是把当前分支master推送到远程
-u:如果当前分支与多个主机存在追踪关系,则可以使用 -u 参数指定一个默认主机,这样后面就可以不加任何参数使用git push- 不指定
mymaster:将会删除远程的master分支,这相当于是push了一个空分支到master - 不指定
:master:默认推送到与mymaster有追踪关系的分支,一般为同名分支,没有的话会在远程建一个(git remote show <origin>查看各个分支的追踪关系)
- 不指定
1 | git pull origin master:mymaster |
- 提交远程之前,最好先将远程仓库的文件拉取下来,再
push,以免造成冲突
1 | git fetch <remote> <remote branchname>:<your branchname> |
- 他与pull一样,就拉取远程仓库中有但你没有的文件(不要拉取到当前所在分支)
- 不同的是,fetch并不会自动合并或修改你当前的工作,更安全。 当准备好时你必须手动将其合并入你的工作。
- pull = fetch + merge (FETCH_HEAD)
1 | git fetch origin |
FETCH_HEAD
- 某个branch在服务器上的最新状态.
- 每一个执行过fetch操作的项目都会存在一个FETCH_HEAD列表,这个列表保存在
.git/FETCH_HEAD文件中, 其中每一行对应于远程服务器的一个分支.当前分支指向的FETCH_HEAD, 就是这个文件第一行对应的那个分支 - 一般来说, 存在两种情况:
- 如果没有显式的指定
远程分支, 则远程分支的master将作为默认的FETCH_HEAD. - 如果指定了
远程分支, 就将这个远程分支作为FETCH_HEAD.
- 如果没有显式的指定
–rebase
- 作用是取消掉本地库中刚刚的commit,并把他们接到更新后的版本库之中。
- 克隆远程仓库
1 | git clone git@github-link |
- 如果远程仓库已有文件,可以将其克隆到本地的空仓库中
分支管理
master:主分支
HEAD:指向当前分支,即指向master
原理
- 新建
dev分支:现在修改东西就是dev指针移动了,而HEAD指向当前的dev分支- 此时修改
dev分支,master分支将不会改变
- 此时修改


- 合并分支:改改指针的问题
- 将
dev分支合并到master上,右图为不使用Fast-Forward快进模式时(–no-ff)
- 将


- 删除分支:将dev指针给删掉就行了

操作
- 创建分支:
1 | git checkout (-b) dev |
相当于:
1 | git branch dev //创建分支(会创建一个和当前分支有一样内容的分支) |
1 | git branch |
- 查看当前分支,当前分支上会加上
*
1 | git branch -d dev |
- 删除分支dev
1 | git branch |
1 | git branch --set-upstream-to origin/branchname |
- 修改
pull时的远程分支关联- upstream:将当前分支推送到其上游分支( tracking >是不推荐的同义词对于上游)
- current:将当前分支推送到同名分支
1 | git merge dev |
- 合并分支到当前分支(如:将
dev上的工作结果合并到master上) - 注:两个分支分别都修改了文件的冲突情况下,需要进行手动更改冲突
- 一般合并分支会在
Fast Forward模式,但此模式下删除分支,会丢失掉分支信息; - 如果要强制禁用
Fast Forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。 --no-ff方式的git merge:看的出原来发生过合并,而merge看不出
- 一般合并分支会在
- 分支策略:
- master:最稳定的,用来发布版本;
- dev:不稳定,所有人将自己的修改合并到这个分支上

修改bug:创建新的分支进行修改,修改后合并到dev上,但是因为commit是一次提交所有的暂存区,所以需要将现在在写的,不需要提交的先用stash指令存储起来
git stash:将当前工作现场存储起来,等以后恢复现场工作后继续使用,此时git status非常的干净
查看:git stash list
恢复:git stash apply - 恢复后不删除stash中的内容,需要用 git stash drop 来删除
git stash pop - 恢复的同时删除
git stash apply stash@{0}:恢复指令的stash
feature分支
每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分
强行删除未合并的分支:git branch -D
标签设置
1 | git tag |
标签类型
- 轻量标签(lightweight):像一个不会改变的分支,它只是某个提交的引用
- 附注标签(annotated):是存储在git数据库的一个完整对象,它可以被校验,其中包含打标签者的名字、电子邮件、地址、日期时间,此外还有一个标签信息,并且可以使用GNU Privacy Guard(GPG)签名并验证
创建标签
1 | git tag -a v1.4 -m "指定一条标签信息" |
1 | git show v1.4 |
- 查看标签信息和与之对应的提交信息,如果是轻量标签则只会有提交信息
删除标签
1 | git tag -d v1.4 |
- 本地删除标签后并不会在远程仓库移除,需要执行第二条指令
- 将冒号前面的空值推送到远程标签名,从而高效地删除它
- 或者使用下面的方式更直观的删除
1 | git push origin --delete <tagname> |
共享标签
1 | git push origin v1.4 |
在创建完标签后你必须显式地推送标签到共享服务器上
–tags:这将会把所有不在远程仓库服务器上的标签全部传送到那里
检出标签
1 | git checkout v2.0.0 |
- 查看某个标签所指向的文件版本,但这会是你的仓库处于“分离头指针(detached HEAD)”的状态
- 此状态如果你做了某些更改然后提交它们,标签不会发生变化, 但你的新提交将不属于任何分支,并且将无法访问,除非通过确切的提交哈希才能访问。 因此,如果你需要进行更改,比如你要修复旧版本中的错误,那么通常需要创建一个新分支
$ git checkout -b version2 v2.0.0
- 此状态如果你做了某些更改然后提交它们,标签不会发生变化, 但你的新提交将不属于任何分支,并且将无法访问,除非通过确切的提交哈希才能访问。 因此,如果你需要进行更改,比如你要修复旧版本中的错误,那么通常需要创建一个新分支
变基
问题
遇到下图白色部分,git请求github失败,是因为设置了代理,取消设置即可

- 在命令行执行
git config --global --unset http.proxy(项目根目录下) - 失败的话执行
env GIT_SSL_NO_VERIFY=true git command
- 在命令行执行
SVM
win: tortoisesvn
macOs: SnailSVNLite(app store下载,svn精简版)
简介
操作
问题
检出认证失败显示下图Error时

- 此问题是未使用授权用户导致的
- svn在验证时,默认使用缓存中的账户密码信息,而不是当是输入的账户信息,因此我们需要先清除缓存
- win:setting -> save data -> clean
- macOs:
rm -rf ~/.subversion/auth
- Post title:版本管理
- Post author:Wei Jieyang
- Create time:2020-12-14 15:55:40
- Post link:https://jieyang-wei.github.io/2020/12/14/版本管理/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.

