版本控制工具GIT使用指南
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了版本控制工具GIT使用指南相关的知识,希望对你有一定的参考价值。
前言:git是分布式版本控制系统,由linux创始人亲自设计,目前是最广泛使用的版本控制工具。本文介绍了版本控制系统的发展和GIT历史,并针对GIT安装和常用命令给出了试验。最后还列举了比较全面的指令供参考。
1.历史简介
1.1什么是版本控制
版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。我们对保存着软件源代码的文件作版本控制,但实际上,你可以对任何类型的文件进行版本控制。
如果你是UI或网页设计师,可能会需要保存某一幅图片或页面布局文件的所有修订版本(这或许是你非常渴望拥有的功能),采用版本控制系统(VCS)是个明智的选择。 有了它你就可以将选定的文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。 使用版本控制系统通常还意味着,就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子。 但额外增加的工作量却微乎其微。
1.2本地版本控制系统
许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单,但是特别容易犯错。 有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。
为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
其中最流行的一种叫做RCS。现今许多计算机系统上都还看得到它的踪影。RCS的工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。
1.3集中化的版本控制系统
接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作? 于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。 这类系统,诸如 CVS、Subversion (即SVN)以及Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。 多年以来,这已成为版本控制系统的标准做法。
这种做法带来了许多好处,特别是相较于老式的本地VCS来说。 现在,每个人都可以在一定程度上看到项目中的其他人正在做些什么。 而管理员也可以轻松掌控每个开发者的权限,并且管理一个CVCS要远比在各个客户端上维护本地数据库来得轻松容易。
事分两面,有好有坏。 这么做最显而易见的缺点是中央服务器的单点故障。 如果宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。 如果中心数据库所在的磁盘发生损坏,又没有做恰当备份,毫无疑问你将丢失所有数据——包括项目的整个变更历史,只剩下人们在各自机器上保留的单独快照。 本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
1.4分布式版本控制系统
于是,分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 在这类系统中,像Git、Mercurial、Bazaar以及Darcs 等,客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。这样就可以在同一个项目中,分别和不同工作小组的人相互协作。你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。
1.5 GIT的产生
同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代。
Linux 内核开源项目有着为数众多的参与者。 绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。 到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。
到了2005 年,开发BitKeeper 的商业公司同Linux内核开源社区的合作关系结束,他们收回了Linux 内核社区免费使用BitKeeper 的权力。 这就迫使Linux开源社区(特别是 Linux 的缔造者Linus Torvalds)基于使用BitKeeper 时的经验教训,开发出自己的版本系统。 他们对新的系统制订了若干目标:
- 速度
- 简单的设计
- 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
- 完全分布式
- 有能力高效管理类似Linux内核一样的超大规模项目(速度和数据量)
自诞生于2005年以来,Git日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。 它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理能力。
1.6GIT是什么?
那么,简单地说,Git 究竟是怎样的一个系统呢? 请注意接下来的内容非常重要,若你理解了 Git 的思想和基本工作原理,用起来就会知其所以然,游刃有余。 在学习 Git 时,请尽量理清你对其它版本管理系统已有的认识,如 CVS、SVN或Perforce, 这样能帮助你使用工具时避免发生混淆。尽管Git用起来与其它的版本控制系统非常相似, 但它在对信息的存储和认知方式上却有很大差异,理解这些差异将有助于避免使用中的困惑。
1.6.1直接记录快照,而非差异比较
Git 和其它版本控制系统(包括 SVN和近似工具)的主要差别在于Git对待数据的方式。 从概念上来说,其它大部分系统以文件变更列表的方式存储信息,这类系统(CVS、Subversion、Perforce、Bazaar 等等) 将它们存储的信息看作是一组基本文件和每个文件随时间逐步累积的差异(它们通常称作基于差异(delta-based)的版本控制)。如下图存储每个文件与初始版本的差异示意图,其中checkins Over Time表示入库时间线。
Git不按照以上方式对待或保存数据。反之,Git更像是把数据看作是对小型文件系统的一系列快照。 在Git中,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照并保存这个快照的索引。 为了效率,如果文件没有修改,Git不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git对待数据更像是一个快照流。下图是存储项目随时间改变的快照示意图,其中checkins Over Time表示入库时间线。
这是Git与几乎所有其它版本控制系统的重要区别。 因此Git重新考虑了以前每一代版本控制系统延续下来的诸多方面。Git更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的VCS。
1.6.2 近乎所有操作都是本地执行
在Git中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。 如果你习惯于所有操作都有网络延时开销的集中式版本控制系统,Git在这方面会让你感到速度之神赐给了Git超凡的能量。 因为你在本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成。
举个例子,要浏览项目的历史,Git不需外连到服务器去获取历史,然后再显示出来——它只需直接从本地数据库中读取。 你能立即看到项目历史。如果你想查看当前版本与一个月前的版本之间引入的修改,Git会查找到一个月前的文件做一次本地的差异计算,而不是由远程服务器处理或从远程服务器拉回旧版本文件再来本地处理。
这也意味着你在离线或者没有VPN时,几乎可以进行任何操作。 如你在飞机或火车上想做些工作,就能愉快地提交(这里指提交到你的本地 副本), 直到有网络连接时再上传。如你回家后VPN客户端不正常,那么也仍能工作。 使用其它系统的话,做到这些是不可能或很费力的。 比如,用Perforce的话,没有连接服务器时几乎不能做什么事;而用SVN和CVS 的话, 你能修改文件,但不能向数据库提交修改(因为你的本地数据库离线了)。
1.6.3 Git 保证完整性
Git 中所有的数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。
Git 用以计算校验和的机制叫做 SHA-1散列(hash)。 这是一个由 40 个十六进制字符(0-9 和 a-f)组成的字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希散列看起来是这样: 24b9da6552252987aa493b52f8696cd6d3b00373
Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值。 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
1.6.4 Git 一般只添加数据
你执行的Git 操作,几乎只往Git 数据库中添加数据。 你很难使用Git 从数据库中删除数据,也就是说Git 几乎不会执行任何可能导致文件不可恢复的操作。 同别的VCS 一样,未提交更新时有可能丢失或弄乱修改的内容。但是一旦你提交快照到Git 中, 就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。
1.6.5三种状态
Git 有三种状态,你的文件可能处于其中之一: 已提交(committed)、已修改(modified) 和 已暂存(staged)。
- 已修改表示修改了文件,但还没保存到数据库中。
- 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
- 已提交表示数据已经安全地保存在本地数据库中。
这会让我们的Git 项目拥有三个阶段:工作区(working directory)、暂存区(staging area)以及仓库区(.git directory),下图是示意图。
工作区是对项目的某个版本独立提取出来的内容。 这些从Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
暂存区是一个文件,保存了下次将要提交的文件列表信息,一般在Git 仓库目录中。 按照Git 的术语叫做“索引”,不过一般说法还是叫“暂存区”。
仓库区是Git 用来保存项目的元数据和对象数据库的地方。 这是Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。
基本的Git 工作流程如下:
- 在工作区中修改文件。
- 将你想要下次提交的更改选择性地暂存,这样只会将更改的部分添加到暂存区。
- 提交更新,找到暂存区的文件,将快照永久性存储到仓库区。
如果仓库区中保存着特定版本的文件,就属于已提交 状态。 如果文件已修改并放入暂存区,就属于已暂存 状态。 如果自上次检出后,作了修改但还没有放到暂存区域,就是已修改 状态。
2 安装和配置
2.1服务器安装配置
(1)下载
https://bitbucket.org/sdorra/scm-manager/downloads/
获得源码sdorra-scm-manager-39150d7e6dfc.zip
解压到:D:\\sdorra-scm-manager-39150d7e6dfc
https://www.scm-manager.org/download/
获得windows安装文件:scm-server-1.60-app.zip
解压到:D:\\scm-server
(2)安装
对于源码,使用mvn clean install 编译后使用java –jar 运行。
对于安装文件,使用scm-server.bat install安装服务,使用net start scm-server启动服务。默认用户名和密码都是scmadmin
(3)配置
修改%userprofile%\\.scm\\config\\server-config.xml文件:
(a)端口改为8000,以免和其他web冲突
<SystemProperty name="jetty.port" default="8000" />
(b)增加用户
使用http://localhost:8000/进入管理门户,进入users菜单,点按钮Add:
2.2客户端安装配置
(1)git安装
下载https://git-scm.com/
文件:Git-2.18.0-64-bit.exe,安装到e:\\git
若只是使用命令行,则这个够用了,否则需要安装TortoiseGit。
(2)TortoiseGit安装
下载https://tortoisegit.org/download/
文件:TortoiseGit-2.7.0.0-64bit.msi,安装到:C:\\Program Files\\TortoiseGit
3.管理流程最佳实践
3.1常用操作
下面针对最常使用的操作进行试验:
(1)启动仿linux外壳
执行开始菜单“git bash”,然后进入指定目录
或者先进入指定目录后,执行右键菜单“git bash here”
(2)把目录添加到本地版本库
依次执行:
git init
git add .
git commit -m “new, purpose: starting project”
(3)在远程库上增加仓库Repository
添加后找到地址:http://localhost:8000/scm/git/setting
(4)新增本地分支并切换过去
依次执行:
git branch #查看本地分支
git branch -r #查看远程分支
git branch “setting” #新建本地分支setting
git checkout “setting” #切换到本地分支setting
git branch #查看本地分支
一步到位为的指令:
Git checkout -b “setting”
(5)上传本地仓库到远程仓库
依次执行:
git remote add origin http://localhost:8000/scm/git/config #新增远程仓库origin
git branch -r #查看远程仓库
git push -u origin setting #同步本地仓库setting到远程仓库origin
重新修改远程仓库:
3.2单主干管理流程
单主干的分支实践(TBD:Trunk-based development)在 SVN 中比较流行,Git也是适用的。Google 和 Facebook 都使用这种方式。trunk 是 SVN 中主干分支的名称,对应到 Git 中则是 master 分支。TBD 的特点是所有团队成员都在单个主干分支上进行开发。当需要发布时,先考虑使用标签(tag),即 tag 某个 commit 来作为发布的版本。如果仅靠 tag 不能满足要求,则从主干分支创建发布分支。bug 修复在主干分支中进行,再 cherry-pick 到发布分支。下面是 TBD 中分支流程的示意图,其中commit表示提交,branch表示分支,cherry-pick表示把指定提交合并到当前分支,release表示发布版本。
由于所有开发人员都在同一个分支上工作,团队需要合理的分工和充分的沟通来保证不同开发人员的代码尽可能少的发生冲突。持续集成和自动化测试是必要的,用来及时发现主干分支中的bug。因为主干分支是所有开发人员公用的,一个开发人员引入的bug 可能对其他很多人造成影响。不过好处是由于分支所带来的额外开销非常小。开发人员不需要频繁在不同的分支之间切换。
3.3 GitHub管理流程
GitHub流程是GitHub所使用的一种简单的流程。该流程只使用两类分支,并依托于 GitHub 的pull request 功能。在 GitHub flow 中,master 分支中包含稳定的代码。该分支已经或即将被部署到生产环境。master 分支的作用是提供一个稳定可靠的代码基础。任何开发人员都不允许把未测试或未审查的代码直接提交到 master 分支。
对代码的任何修改,包括bug 修复、hotfix、新功能开发等都在单独的分支中进行。不管是一行代码的小改动,还是需要几个星期开发的新功能,都采用同样的方式来管理。当需要进行修改时,从master 分支创建一个新的分支。新分支的名称应该简单清晰地描述该分支的作用。所有相关的代码修改都在新分支中进行。开发人员可以自由地提交代码和push 到远程仓库。
当新分支中的代码全部完成之后,通过GitHub 提交一个新的pull request。团队中的其他人员会对代码进行审查,提出相关的修改意见。由持续集成服务器(如Jenkins)对新分支进行自动化测试。当代码通过自动化测试和代码审查之后,该分支的代码被合并到master 分支。再从master 分支部署到生产环境。下面是GitHub流程的示意图,其中commit表示提交,branch表示分支,merge表示合并到当前分支,release表示发布版本。
GitHub流程的好处在于非常简单实用。开发人员需要注意的事项非常少,很容易形成习惯。当需要进行任何修改时,总是从流程要求项目有完善的自动化测试、持续集成和部署等相关的基础设施。每个新分支都需要测试和部署,如果这些不能自动化进行,会增加开发人员的工作量,导致无法有效地实施该流程。这种分支实践也要求团队有代码审查的相应流程。
3.4长周期管理流程
长周期流程应该是目前流传最广的版本控制流程。长周期流程围绕的核心概念是版本发布(release)。因此长周期流程适用于有较长版本发布周期的项目。
目前推崇的做法是持续集成和即时发布,有的项目甚至可以一天发布很多次,即时发布对于,仍然有大量项目的发布周期是几个星期甚至几个月。较长的发布周期可能是由于非技术相关的因素造成的,比如人员限制、管理层决策和市场营销策略等。
长周期流程中包含5类分支,分别是 master、develop、feature、release和 hotfix。这些分支的作用和生命周期各不相同。五分支的对比表如下:
分类类型 | 命名规范 | 创建自 | 合并到 | 备注 |
master | master/* | 用来发布版本 | ||
develop | develop/* | master | master | 最新代码,日常工作分支 |
feature | feature/* | develop | develop | 新功能开发 |
release | release/* | develop | develop和master | 测试版本提交 |
hotfix | hotfix/* | master | develop和master | 补丁版本提交 |
master 分支中包含的是可以部署到生产环境中的代码,这一点和 GitHub flow 是相同的。develop 分支中包含的是下个版本需要发布的内容。从某种意义上来说,develop 是一个进行代码集成的分支。当 develop 分支集成了足够的新功能和 bug 修复代码之后,通过一个发布流程来完成新版本的发布。发布完成之后,develop 分支的代码会被合并到 master 分支中。
对于开发过程中的不同任务,需要在对应的分支上进行工作并正确地进行合并。每个任务开始前需要按照指定的步骤完成分支的创建。
当开发一个新的功能时,流程大致如下:
- 从develop分支创建一个新的feature分支,如feature/my-awesome-feature。
- 在该feature分支上进行开发,提交代码,push到远端仓库。
- 当代码完成之后,合并到develop分支并删除当前feature分支。
当需要发布新版本时,流程大致如下:
- 从develop分支创建一个新的release分支,如release/1.4。
- 把release分支部署到持续集成服务器上进行测试。测试包括自动化集成测试和手动的用户接受测试。
- 对于测试中发现的问题,直接在release分支上提交修改。完成修改之后再次部署和测试。
- 当release分支中的代码通过测试之后,把release分支合并到develop和master分支,并在master分支上添加相应的tag。
因为长周期流程比较繁琐和难以记忆,在实践中一般使用辅助脚本来完成相关的工作。比如:同样的开发新功能的任务,可以使用git flow feature start my-awesome-feature 来完成新分支的创建,使用git flow feature finish my-awesome-feature 来结束feature 分支。辅助脚本会完成正确的分支创建、切换和合并等工作。
3.5流程选择考量
每个开发团队都应该根据团队自身和项目的特点来选择最适合的管理流程。首先是项目的版本发布周期。如果发布周期较长,则长周期流程是最好的选择。长周期流程可以很好地解决新功能开发、版本发布、生产系统维护等问题;如果发布周期较短,则单主干流程和流程都是不错的选择。GitHub流程的特色在于集成了流程是最佳的选择。GitHub流程和单主干流程对持续集成和自动化测试等基础设施有比较高的要求,如果相关的基础设施不完善,则不建议使用。
4.指令大全
4.1新建代码库
# 在当前目录新建一个Git代码库
$ git init
# 新建一个目录,将其初始化为Git代码库
$ git init [project-name]
# 下载一个项目和它的整个代码历史
$ git clone [url]
4.2配置
Git的设置文件为.gitconfig,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)。
# 显示当前的Git配置
$ git config --list
# 编辑Git配置文件
$ git config -e [--global]
# 设置提交代码时的用户信息
$ git config [--global] user.name "[name]"
$ git config [--global] user.email "[email address]"
4.3增加/删除文件
# 添加指定文件到暂存区
$ git add [file1] [file2] ...
# 添加指定目录到暂存区,包括子目录
$ git add [dir]
# 添加当前目录的所有文件到暂存区
$ git add .
# 添加每个变化前,都会要求确认
# 对于同一个文件的多处变化,可以实现分次提交
$ git add -p
# 删除工作区文件,并且将这次删除放入暂存区
$ git rm [file1] [file2] ...
# 停止追踪指定文件,但该文件会保留在工作区
$ git rm --cached [file]
# 改名文件,并且将这个改名放入暂存区
$ git mv [file-original] [file-renamed]
4.4代码提交
# 提交暂存区到仓库区
$ git commit -m [message]
# 提交暂存区的指定文件到仓库区
$ git commit [file1] [file2] ... -m [message]
# 提交工作区自上次commit之后的变化,直接到仓库区
$ git commit -a
# 提交时显示所有diff信息
$ git commit -v
# 使用一次新的commit,替代上一次提交
# 如果代码没有任何新变化,则用来改写上一次commit的提交信息
$ git commit --amend -m [message]
# 重做上一次commit,并包括指定文件的新变化
$ git commit --amend [file1] [file2] ...
4.5分支
# 列出所有本地分支
$ git branch
# 列出所有远程分支
$ git branch -r
# 列出所有本地分支和远程分支
$ git branch -a
# 新建一个分支,但依然停留在当前分支
$ git branch [branch-name]
# 新建一个分支,并切换到该分支
$ git checkout -b [branch]
# 新建一个分支,指向指定commit
$ git branch [branch] [commit]
# 新建一个分支,与指定的远程分支建立追踪关系
$ git branch --track [branch] [remote-branch]
# 切换到指定分支,并更新工作区
$ git checkout [branch-name]
# 切换到上一个分支
$ git checkout -
# 建立追踪关系,在现有分支与指定的远程分支之间
$ git branch --set-upstream [branch] [remote-branch]
# 合并指定分支到当前分支
$ git merge [branch]
# 选择一个commit,合并进当前分支
$ git cherry-pick [commit]
# 删除分支
$ git branch -d [branch-name]
# 删除远程分支
$ git push origin --delete [branch-name]
$ git branch -dr [remote/branch]
4.6标签
Git版本控制工具学习