充分了解 Git 并入手单人开发

作者: hylerrix | 来源:发表于2016-11-15 19:50 被阅读1006次

    前注1:本文写作用时 5 小时 55 min,预计阅读时间 15 分钟。
    前注2:本文基于学习型写作。查阅+写作+总结+迭代同步进行。


    ARTICLE.INTRODUCTION.序


    Git 作为以命令行为主的免费开源分布式版本控制系统,说简单也简单,说难也绝非易事。本文以快速上手 Git 单人开发为最终目标,贯穿讲解 Git 背景等相关知识,内容较长,却也循序渐进,以期达到融汇贯通的效果,不足之处还望指点。在看本文之前,你最好能够提前了解 Linux 基本命令的使用。


    ABOUT.VCS.了解版本控制系统(VCS)


    本地版本控制系统(LVCS)

    为了解决复制整个项目目录的方式来保存不同的版本问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。

    其中最流行的 RCS 的工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化);通过应用所有的补丁,可以重新计算出各个版本的文件内容。

    甚至在流行的 Mac OS X 系统上安装了开发者工具包之后,也可以使用 rcs 命令。

    集中化版本控制系统(CVCS)

    接下来的问题是,如何让在不同系统上的开发者协同工作?于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。这类系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。

    这种做法带来了许多好处:每个人都可以在一定程度上看到项目中的其他人正在做些什么,而管理员也可以轻松掌控每个开发者的权限。但考虑到中央服务器的单点故障、中心数据库所在的磁盘发生损坏等问题,整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。

    分布式版本控制系统(DVCS)

    于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 在这类系统中,像 Git、Mercurial、Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。

    更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。 你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。


    GET.START.GIT 简史


    Git 的由来

    同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代。

    Linux 内核作为参与人数众广的开源项目,绝大多数维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。 到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码,却于 2005 年被开发 BitKeeper 的商业公司结束合作关系,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linux Torvalds)基于使用 BitKcheper 时的经验教训,开发出自己的版本系统 -- Git。 他们对新的系统制订了若干目标来能高效管理类似 Linux 内核一样的超大规模项目(速度和数据量):

    • 速度高
    • 设计简约
    • 对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
    • 完全分布式

    自诞生于 2005 年以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。 它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理系统(Git 分支)。

    谁在使用 Git

    以下是官网上列出的使用 Git 的著名公司、项目,但远不止这些。


    DEEP.IN.深入Git


    因此,Git 是分布式版本控制系统中很流行的一种, 和其它大部分系统以文件变更列表的方式存储信息对比:后者将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异,而 Git 更像是把数据看作是对小型文件系统的一组快照。

    Git 对待数据更像是一个快照流。

    Git 的三个特点

    • 近乎所有操作都是本地执行
      如果你习惯于所有操作都有网络延时开销的集中式版本控制系统,Git 在这方面会让你感到速度之神赐给了 Git 超凡的能量。 因为你在本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成。
      • Git 不需外连到服务器去获取历史,只需要从本地数据库中读取。
      • 直接能从本地计算当前版本与一个月前的版本之间引入的修改。
    • Git 保证完整性
      Git 中所有数据在存储前都计算校验和(Git 使用哈希值),然后以校验和来引用。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。
      • 哈希值看起来是这样: 24b9da6552252987aa493b52f8696cd6d3b00373
      • 实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
    • Git 一般只添加数据
      你执行的 Git 操作,几乎只往 Git 数据库中增加数据。未提交更新时有可能丢失或弄乱修改的内容,但是一旦你提交快照到 Git 中,就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。

    Git 的三种状态

    Git 系统上的文件有三种状态已修改(modified)和已暂存(staged)、已提交(committed)。

    • 已修改表示修改了文件,但还没保存到数据库中。
    • 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
    • 已提交表示数据已经安全的保存在本地数据库中。

    这里便是 git add, git commit, git push 的原理所在。

    由此引入 Git 项目的三个工作区域的概念:

    • Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。
    • 工作目录是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
    • 暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。 有时候也被称作“索引”,不过一般说法还是叫暂存区域。

    Git 的工作流程

    • 在工作目录中修改文件。
    • (git add)暂存文件,将文件的快照放入暂存区域。
    • (git commit)提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。

    可以用如下命令随时查看 Git 系统中文件的状态:

    git status
    

    USE.WAY.Git 使用方式


    Git 有多种使用方式。 你可以使用原生的命令行模式,也可以使用 GUI 模式,这些 GUI 软件也能提供多种功能。以下分别介绍这两种模式,首推命令行模式。

    使用 Git 的命令行工具【推荐】

    只有在命令行模式下你才能执行 Git 的所有命令,而大多数的 GUI 软件只实现了 Git 所有功能的一个子集以降低操作难度。此外,由于每个人的想法与侧重点不同,不同的人常常会安装不同的 GUI 软件,但所有人一定会有命令行工具。

    • 在 Linux 上安装 Git
      如果你想在 Linux 上用二进制安装程序来安装 Git,可以使用发行版包含的基础软件包管理工具来安装。 以下二选一,视具体发行版本而定。
    sudo yum install git
    
    sudo apt-get install git
    

    安装 Git 的 GUI 工具

    这里只做 Github Desktop 的介绍和推荐,有关 Github 的相关知识点之后提及。

    可以看出,Git 的图形化操作比命令行操作用起来要方便也更加直观。多平台的 Github Desktop 将 Git 的图形化操作发挥到了极致。

    但重回之前所说,只有在命令行模式下你才能执行 Git 的所有命令,而大多数的 GUI 软件只实现了 Git 所有功能的一个子集以降低操作难度。Git 的 GUI 对使用分布式版本控制的开发者来说还无法普及。


    PLATFORM.TOOL.基于 Git 的 Github 代码托管平台


    GitHub 是一个面向开源及私有软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,故名 GitHub。GitHub 于 2008 年 4 月 10 日正式上线,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。目前,其注册用户已经超过350万,托管版本数量也是非常之多,其中不乏知名开源项目 Ruby on Rails、jQuery 等。

    放入自己的开源项目,实战的同时还能帮助需要的人,多么有意义。


    SINGLE.DEVELOPMENT.实战 Git 单人开发


    于是到了实战环节,基于命令行模式,开始我们的 Git 单人开发。

    01.配置 Git 基本信息

    Git 自带一个 git config 的工具来帮助设置控制 Git 外观和行为的配置变量。 这些变量存储在三个不同的位置:

    • /etc/gitconfig 文件: 包含系统上每一个用户及他们仓库的通用配置。 如果使用带有 --system 选项的 git config 时,它会从此文件读写配置变量。
    • ~/.gitconfig 或 ~/.config/git/config 文件:只针对当前用户。 可以传递 --global 选项让 Git 读写此文件。
    • 当前使用仓库的 Git 目录中的 config 文件(就是 .git/config):针对该仓库。

    每一个级别覆盖上一级别的配置,所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。

    在 Windows 系统中,Git 会查找 $HOME 目录下(一般情况下是 C:\Users$USER )的 .gitconfig 文件。 Git 同样也会寻找 /etc/gitconfig 文件,但只限于 MSys 的根目录下,即安装 Git 时所选的目标位置。

    如上图,安装好 Git 之后就应该去配置自己的用户名和邮箱号,因为每一个 Git 的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改。很多 GUI 工具都会在第一次运行时帮助你配置这些信息。

    git config --global user.name "hylerrix"
    
    git config --global user.email "hylerrix@gmail.com"
    
    git list
    

    02.添加 Git 忽略文件

    有些时候,你必须把某些文件放到Git工作目录中,但又不能提交它们,比如保存了数据库密码的配置文件,再比如Node 模块也是这样,只需把项目所需依赖名添加至 package.json 中,克隆到本地后本地安装依赖即可。

    这就需要用到 Git 的 .gitignore 文件。Git 对于 .ignore 配置文件是按行从上到下进行规则匹配的,意味着如果前面的规则匹配的范围更大,则后面的规则将不会生效。下面列举几个常用的配置语法。

    • 以斜杠“/”开头表示目录;
    • 以星号“*”通配多个字符;
    • 以问号“?”通配单个字符
    • 以方括号“[]”包含单个字符的匹配列表;
    • 以叹号“!”表示不忽略(跟踪)匹配到的文件或目录;

    .gitignore 文件一般不存在,需要在仓库根目录下手动创建。

    03.创建本地仓库

    选择工作目录创建仓库,这里命名为“gittest”。

    mkdir gittest
    

    进入 gittest 文件夹

    cd gittest
    

    04.初始化版本库

    新建的文件夹并不是 git 仓库,需要在根目录里面进行初始化。

    git init
    

    初始化正常情况下应该得到如下命令行反馈:

    Initialized empty Git repository in <Git 仓库文件所对应的绝对路径>
    

    初始化成功会发现新的 Git 仓库中多出了一个 .git 隐藏文件夹。用编辑器打开可以看到,.git 隐藏文件夹是 git init 后在当前目录生成的一个管理 git 仓库的文件夹,这里存储着 git 你现在检出(checkout)的文件,当你在项目不同分支切换时,工作目录里的文件经常会被替换和删除。所有历史信息都保存在当前目录中。

    • hooks:这个目录存放一些 shell 脚本,可以设置特定的git命令后触发相应的脚本;在搭建 gitweb 系统或其他 Git 托管系统会经常用到 hook script。
    • info:包含仓库的一些信息
    • logs:保存所有更新的引用记录
    • objects:该目录存放所有的Git对象,对象的SHA1哈希值的前两位是文件夹名称,后38位作为对象文件名。
    • refs:具体的引用,Reference Specification,这个目录一般包括三个子文件夹,heads、remotes和tags,比如,heads中的master文件标识了项目中的master分支指向的当前commit,其他类似。
    • COMMIT_EDITMSG:保存最新的commit message,Git系统不会用到这个文件,只是给用户一个参考
    • config:这个是GIt仓库的配置文件
    • description:仓库的描述信息,主要给gitweb等git托管系统使用
    • index:这个文件就是我们前面提到的暂存区(stage),是一个二进制文件
    • HEAD:这个文件包含了一个分支(branch)的引用,通过这个文件Git可以得到下一次commit的parent
    • ORIG_HEAD:HEAD指针的前一个状态

    05.添加与提交

    之前的两步已经建立好了一个空的 Git 仓库,开始添加新文件。

    touch README.md
    
    git add README.md
    
    git commit -m "init"
    

    06.创建和删除分支

    几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。和许多其他版本控制系统不同,Git 鼓励在工作流程中频繁使用分支与合并,哪怕一天之内进行许多次都没有关系。在创建仓库的时候,master 是“默认的”。在其他分支上进行开发,完成后再将它们合并到主分支上。

    创建一个叫做“feature_x”的分支,并切换过去:

    git checkout -b feature_X
    

    切换回主分支:

    git checkout master
    

    再把新建的分支删掉:

    git branch -d feature_x
    

    分支的版本回退:

    git reset HEAD~3
    

    除非你将分支推送到远端仓库,不然该分支就是不为他人所见的:

    git push origin master
    

    07.更新与合并

    要更新你的本地仓库至最新的改动,执行如下命令以在工作目录中获取(fetch)并合并(merge)远端的改动。

    git pull
    

    要合并其他分支到你的当前分支,可以执行如下命令。

    git merge <branch>
    

    两种情况下,git 都会尝试去自动合并改动。不幸的是,这可能导致产生需要自己手动合并的冲突(conflicts)。改完冲突后,执行以下命令标记合并成功:

    git add <filename>
    

    在合并改动之前,可以使用如下命令查看:

    git diff <source_branch> <target_branch>
    

    08.日志操作

    大雁塔酒店。

    git log
    

    09.撤销与修改

    用来撤销最后一次 git add files,你也可以用 git reset 撤销所有暂存区域文件。

    git reset -- files
    

    把文件从暂存区域复制到工作目录,用来丢弃本地修改。

    git checkout -- files
    

    通常,一个合并会产生一个合并提交(commit),把两个父分支里的每一行都合并起来。

    git reset --hard HEAD
    

    修改 git commit 时填错的错误信息

    git commit --amend
    

    但是,如果当前分支和另一个分支没有内容上的差异(即冲突),git就会执行一个"快速向前(fast-forward)"操作,git不会创建新的提交,只是将当前分支指向合并进来的分支。

    10.生成 SSH 密钥

    为了使本地仓库和远端安全链接,于是到了这一步。

    SSH key提供了一种与 GitHub 通信的方式,通过这种方式,能够在不输入密码的情况下,将GitHub作为自己的remote端服务器,进行版本控制。分为如下三步。

    • 查看 SSH 密钥是否存在,如果有则备份删除。
    cd ~/.ssh
    
    • 生存密钥,过程中可直接按回车。
    ssh-keygen -t rsa -C "hylerrix@gmail.com"
    

    最后得到两个文件:id_rsa 和 id_rsa.pub 并获取其中密钥

    cat ~/.ssh/id_rsa.pub
    
    • 在代码托管平台(这里是 Github )中添加公钥。


    11.创建远端仓库

    这里用到的是对 Git 版本控制系统运用最好的全球最大代码托管平台兼同性交友平台 -- Github。创建好自己的 Github 账号并邮箱验证后便可以创建 Git 远端新仓库。这里创建一个和本地同名的仓库“gittest”。

    12.绑定本地到远端仓库

    由于之前的操作都只是本地的,没有对远端仓库进行任何绑定,需要如下命令。

    git remote add origin https://github.com/hylerrix/gittest.git
    

    这里链接到了我的 github 地址的 gittest 仓库。这时我的仓库是这样子的,什么都没有。

    13.推送改动

    在进行 git commit -m "" 后所有改动已经在本地仓库的 HEAD 中了。执行如下命令以将这些改动提交到远端仓库:

    git push origin master
    

    再次刷新 github 远端仓库,发现改动推送成功。

    14.克隆远端仓库

    从执行如下命令以创建一个本地仓库的克隆版本。

    git clone /path/to/repository
    

    如果是远端服务器上的仓库,你的命令会是这个样子。

    git clone username@host:/path/to/repository
    

    git 常用命令清单


    git init # 初始化本地git仓库(创建新仓库)
    git config --global user.name "xxx" # 配置用户名
    git config --global user.email "xxx@xxx.com" # 配置邮件
    git config --global color.ui true # 
    git status等命令自动着色
    git config --global color.status autogit config --global color.diff autogit config --global color.branch autogit config --global color.interactive autogit config --global --unset http.proxy # remove proxy configuration on gitgit clone git+ssh://git@192.168.53.168/VT.git # clone远程仓库
    git status # 查看当前版本状态(是否修改)
    git add xyz # 添加xyz文件至indexgit add . # 增加当前子目录下所有更改过的文件至index
    git commit -m 'xxx' # 提交
    git commit --amend -m 'xxx' # 合并上一次提交(用于反复修改)
    git commit -am 'xxx' # 将add和commit合为一步
    git rm xxx # 删除index中的文件
    git rm -r * # 递归删除
    git log # 显示提交日志
    git log -1 # 显示1行日志 -n为n行
    git log -5
    git log --stat # 显示提交日志及相关变动文件
    git log -p -m
    git show dfb02e6e4f2f7b573337763e5c0013802e392818 # 显示某个提交的详细内容
    git show dfb02 # 可只用commitid的前几位
    git show HEAD # 显示HEAD提交日志
    git show HEAD^ # 显示HEAD的父(上一个版本)的提交日志 ^^为上两个版本 ^5为上5个版本
    git tag # 显示已存在的tag
    git tag -a v2.0 -m 'xxx' # 增加v2.0的tag
    git show v2.0 # 显示v2.0的日志及详细内容
    git log v2.0 # 显示v2.0的日志
    git diff # 显示所有未添加至index的变更
    git diff --cached # 显示所有已添加index但还未commit的变更
    git diff HEAD^ # 比较与上一个版本的差异
    git diff HEAD -- ./lib # 比较与HEAD版本lib目录的差异
    git diff origin/master..master # 比较远程分支master上有本地分支master上没有的
    git diff origin/master..master --stat # 只显示差异的文件,不显示具体内容
    git remote add origin git+ssh://git@192.168.53.168/VT.git # 增加远程定义(用于push/pull/fetch)
    git branch # 显示本地分支
    git branch --contains 50089 # 显示包含提交50089的分支
    git branch -a # 显示所有分支
    git branch -r # 显示所有原创分支
    git branch --merged # 显示所有已合并到当前分支的分支
    git branch --no-merged # 显示所有未合并到当前分支的分支
    git branch -m master master_copy # 本地分支改名
    git checkout -b master_copy # 从当前分支创建新分支master_copy并检出
    git checkout -b master master_copy # 上面的完整版
    git checkout features/performance # 检出已存在的features/performance分支git checkout --track hotfixes/BJVEP933 # 检出远程分支hotfixes/BJVEP933并创建本地跟踪分支
    git checkout v2.0 # 检出版本v2.0
    git checkout -b devel origin/develop # 从远程分支develop创建新本地分支devel并检出
    git checkout -- README # 检出head版本的README文件(可用于修改错误回退)
    git merge origin/master # 合并远程master分支至当前分支
    git cherry-pick ff44785404a8e # 合并提交ff44785404a8e的修改
    git push origin master # 将当前分支push到远程master分支
    git push origin :hotfixes/BJVEP933 # 删除远程仓库的hotfixes/BJVEP933分支
    git push --tags # 把所有tag推送到远程仓库
    git fetch # 获取所有远程分支(不更新本地分支,另需merge)
    git fetch --prune # 获取所有原创分支并清除服务器上已删掉的分支
    git pull origin master # 获取远程分支master并merge到当前分支
    git mv README README2 # 重命名文件README为README2
    git reset --hard HEAD # 将当前版本重置为HEAD(通常用于merge失败回退)
    git rebase
    git branch -d hotfixes/BJVEP933 # 删除分支hotfixes/BJVEP933(本分支修改已合并到其他分支)
    git branch -D hotfixes/BJVEP933 # 强制删除分支hotfixes/BJVEP933git ls-files # 列出
    git index包含的文件
    git show-branch # 图示当前分支历史
    git show-branch --all # 图示所有分支历史
    git whatchanged # 显示提交历史对应的文件修改
    git revert dfb02e6e4f2f7b573337763e5c0013802e392818 # 撤销提交dfb02e6e4f2f7b573337763e5c0013802e392818
    git ls-tree HEAD # 内部命令:显示某个git对象
    git rev-parse v2.0 # 内部命令:显示某个ref对于的SHA1 HASHgit reflog # 显示所有提交,包括孤立节点
    git show HEAD@{5}
    git show master@{yesterday} # 显示master分支昨天的状态
    git log --pretty=format:'%h %s' --graph # 图示提交日志
    git show HEAD~3
    git show -s --pretty=raw 2be7fcb476git stash # 暂存当前修改,将所有至为HEAD状态
    git stash list # 查看所有暂存
    git stash show -p stash@{0} # 参考第一次暂存
    git stash apply stash@{0} # 应用第一次暂存
    git grep "delete from" # 文件中搜索文本“delete from”git grep -e '#define' --and -e SORT_DIRENTgit gcgit fsck
    

    相关文章

      网友评论

      • SmNiuhe:作者很棒,喜欢详细的文章
        hylerrix:@SmNiuhe 谢谢。原本想命名《Git 从入门到缘来如此》最后考虑到还有 Git 多人协作、Git 对象模型等等才拆分出了这一篇。:blush:也就是说 Git 还能再深入哦。
      • 戴少:作者你好,我可以把ide的工作环境改为git本地仓库吗。我用的是idea
        hylerrix:@戴少 嗯嗯😊
        戴少:@icorvoh 谢谢回复,看好你哦
        hylerrix:@戴少 很抱歉回复的晚。IDEA 可以改成 Git 目录并在里面管理 GIt 的。安装好 Git 环境在 IDEA 里配置绑定即可。Settings——Version Control——Git——Path to Git executable。。。这篇文章有更多:https://my.oschina.net/lujianing/blog/194069
      • 6d96978eeefb:Git 的三种状态
        Git 系统上的文件有三种状态已提交(committed)、已修改(modified)和已暂存(staged)。

        这里三个词对应的状态之间是有顺序的,建议把已提交放在最后,这样读起来更流畅。
        6d96978eeefb:@icorvoh 你下面的说明还没改呢

        > 已提交表示数据已经安全的保存在本地数据库中。
        > 已修改表示修改了文件,但还没保存到数据库中。
        > 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中
        6d96978eeefb:@icorvoh 因为要写一个git相关的任务,参考了你的文章
        hylerrix:@TW李鹏 好的已修改,老师真细心 :smiley: :+1: :smile:
      • 半生不熟_:👍👍👍
        hylerrix:@半生不熟的點不小 :relieved::relieved::relieved:

      本文标题:充分了解 Git 并入手单人开发

      本文链接:https://www.haomeiwen.com/subject/uaokpttx.html