版本控制之Git初探

2,329 阅读10分钟

作为一名程序员,肯定会将人生大部分的时光交给了 Github,这个戏称为全球最大的同性交友网站。Why?因为程序员需要版本控制系统(VCS)来对代码进行管理。这样程序员就可以跟踪项目的变化细节,将选定的文件回溯到之前某个状态,以及各个成员在多人协同项目中的代码贡献等。本文是对版本控制软件 Git 的初步探索,完全可以支持日常多人协同开发。

简介

版本控制系统

版本控制系统Version Control System)是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。它不仅可以用于软件源代码的管理,而且对绝大多部分的文件(二进制文件不支持增量提交)进行版本控制。 它的发展经历了三个阶段,分别是本地化,集中化和分布式。相较于最简单粗暴的复制整个项目来保存不同版本的原始做法,这三个阶段在不断优化前者的劣势使版本控制这事越来越完善。

  • 本地化版本控制系统
    本地化版本控制系统采用某种简单的数据库来记录文件的历次更新差异。它解决了原始粗暴版本系统中备份文件太大,对比很不方便,不易管理等问题。其中典型代表叫 RCS, 它的工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化,也叫差分编码);通过应用所有的补丁,可以重新计算出各个版本的文件内容。

TIPS 差分编码Delta encoding),又称增量编码,是指在序列式资料之间以数据差异形式存储或发送资料的方式(相对于存储发送完整文件的方式)。这种方法应用很广泛,像查看文件的历史更改记录(版本控制系统:RCS,SVN,Git)、Windows 中的远程差分压缩、在线备份、Mysql 的主从同步机制、Redis 的 AOF 持久化等。

  • 集中化版本控制系统
    集中化版本控制系统(Centralized Version Control System)使用单一的集中管理的服务器来保存所有文件的修订版本,而协同工作的人都通过客户端连到这台服务器,获取最新的文件或者提交更新。它解决了多人协同的问题。其典型的代表是 SVN,CVS 以及 Perforce 等。

  • 分布式版本控制系统
    分布式版本控制系统(Distributed Version Control System)除了在服务端保留完整的历史版本信息,在任何一个客户端都保留了项目完整的历史记录。它解决的就是服务器单点故障或者中心数据库磁盘损坏的问题。其典型的代表是 Git,Mercurial 等。

Git简介

Git 是一个分布式版本控制软件,最初由林纳斯·托瓦兹创作,于2005年以 GPL 协议发布。最初的目的是为了更好的管理 Linux 内核开发而设计。优势的是它速度快,以及出色的合并追踪能力。至于它的诞生有个小故事。

Git 由来
2002 年到 2005 之间, Linux 内核项目一直无偿使用一个叫 BitKeeper 的专有分布式版本控制系统来管理和维护代码。因为一个叫安德鲁·垂鸠的程序员写了一个简单程序,能够连接 BitKeeper 的存储库,引起了 BitKeeper 著作权拥有者不满,收回了 Linux 内核社区免费使用 BitKeeper 的权利。协商无果后,林纳斯·托瓦兹用十天时间编写了 Git 的第一个版本来代替 BitKeeper 来管理和维护 Linux 内核源码。

Git 在管理项目时,在本地会有三个工作区域:Git 的工作区、暂存区和本地仓库。如下图所示:git-three-workspace.

  • 工作区(Working Directory)
    工作区是指项目目录中除了 .git 文件夹之外的所有文件。在该工作区中的文件只有两种状态:已跟踪文件和未跟踪文件。而已跟踪文件又细分为已提交(committed),已修改(modified)和已暂存(staged)这三种状态。 文件状态如下图所示: git-file-status
  • 暂存区(Stage/Index Area)
    暂存区是工作区到本地仓库的一个过渡区。对应 .git/index 文件。
  • 本地仓库(Local Repository)
    存放已经提交的数据,push 的时候,就是把这个区的数据 push 到远程仓库了

GitHub简介

GitHu 是最大的 Git 版本库托管商,是成千上万的开发者和项目能够合作进行的中心。 大部分 Git 版本库都托管在 GitHub,很多开源项目使用 GitHub 实现 Git 托管、问题追踪、代码审查以及其它事情。说白一点就是将 GitHub 当做远程仓库,并实现本地仓库和远程仓库的准实时同步。这样即使本地仓库发生异常,也可以通过远程仓库来恢复。

实际中用 Git 对项目进行版本控制的操作图: git-github

图中 workspace 代表当前工作区,index代表暂存区,也叫索引,Repository 代表本地仓库,而 Remote 代表远程服务器上的仓库,这里一般指 GitHub/Gitlab/Gitee 等网站的服务器.

Git文件结构

  • 文件夹:

    • branches
    • hooks 客户端或服务端钩子脚本
    • info 保存了一份不希望在 .gitignore 文件中管理的忽略模式 (ignored patterns) 的全局
    • logs 存储日志的文件夹
    • objects 存放 Git 对象
    • refs 存储指向各个分支的指针(SHA-1标识)文件
  • 文件

    • HEAD 指向当前分支
    • config 包含了项目特有的配置选项
    • description 仅供GitWeb 程序使用
    • index 保存了暂存区信息

Git 操作详解

  • git clone 从远程主机克隆一个版本库

    # 用法 从版本库的网址上拷贝文件到本地文件夹
    git clone <版本库的网址> <本地目录名>
    # 用例 支持多种文件协议
    git clone http[s]://example.com/path/to/repo.git/  # http协议
    git clone ssh://example.com/path/to/repo.git/      # ssh协议
    git clone git://example.com/path/to/repo.git/      # git协议
    git clone /opt/git/project.git                     # 本地文件
    git clone file:///opt/git/project.git              # 本地文件
    git clone ftp[s]://example.com/path/to/repo.git/   # ftp协议
    git clone rsync://example.com/path/to/repo.git/    # rsync协议
    
  • git remote 管理远程主机名

    # 用法
    git remote add <主机名> <网址>    # 添加远程主机名
    git remote rm <主机名>           # 删除远程主机名
    git remote rename <原主机名> <新主机名>   # 更改原主机名
    git remote show <主机名>         # 显示主机名的详细信息
    
  • git fetch 将远程主机的版本库更新取回本地(clone是从无到有,fetch是从有到有

    git fetch <主机名> <分支名>
    
  • git branch/checkout/merge/rebase 管理分支(Git 最大的特色

    # 用法 查看分支情况
    git branch -r    # 查看远程分支
    git branch -a    # 查看所有分支,远程分支以remotes/开头
    # 用法 创建新分支
    git checkout -b newBranch origin/master   # 在origin/master的基础上创建新分支
    # 用法 合并分支
    git merge origin/master      # 将origin/master分支合并到本地分支
    git rebase origin/master
    
  • git pull 取回远程主机某个分支的更新,再与本地的指定分支合并

    git pull <远程主机名> <远程分支名>:<本地分支名>
    # 等价于
    git fetch <远程主机名> <远程分支名>
    git merge <远程主机名>/<远程分支名>
    
  • git push 将本地分支的更新,推送到远程主机

    git push <远程主机名> <本地分支名>:<远程分支名>
    

Linux 下实战(以 Ubuntu 为例)

安装

  • 安装 Git

    sudo apt-get install git
    
  • 配置SSH Keys

    # 以 yuguoliusheng@gmail.com 创建 ssh key,并将私钥保存在 github 文件中,公钥保存在 github.pub 文件中
    ssh-keygen -C "yuguoliusheng@gmail.com" -f ~/.ssh/github
    
  • 配置 GitHub
    登录自己的 GitHub,依次点击 Settings => SSH keys => Add SSH key,将 github.pub 中的公钥添加进去.

  • 验证成功

    # 执行命令
    ssh -T git@github.com
    # 提示
    Hi yuguoliusheng! You've successfully authenticated, but GitHub does not provide shell access.
    
  • 配置git的个人信息

    # 设置用户名
    git config --global user.name "yuguoliusheng"
    # 设置email邮箱
    git config --global user.email "yuguoliusheng@gmail.com"
    

    TIPS: 配置级别:系统/用户/项目仓库

    • 系统级别
      设置/etc/gitconfig文件,对机器上每个用户每个项目仓库都有效 git config --system user.name "yuguoliusheng"
    • 用户级别 设置$HOME/.gitconfig文件,对当前用户的每个项目仓库都有效 git config --global user.name "yuguoliusheng"
    • 项目仓库级别 设置当前项目中 .git/config文件,只对当前项目仓库有效 git config --local user.name "yuguoliusheng" 其优先级别是: 项目仓库级别 > 用户级别 > 系统级别

使用

  • 为已有的项目 project 创建代码仓库

    # 在项目 project 的根目录执行以下命令
    
    # 创建README.md文件
    echo "# Git 初探" >> README.md
    # 创建一个空的 git 版本库(创建 .git 文件夹)
    git init
    # 添加所有文件到暂存区域
    git add *
    # 将暂存区域文件提交到本地仓库,并标注信息“initial project”
    git commit -m "initial project"
    # 添加远程主机名 origin,它代表网址 https://github.com/yuguoliusheng/introduce.git
    git remote add origin https://github.com/yuguoliusheng/introduce.git
    # 将本地的 master/main 分支推送到 origin 主机上的 master/main 分支,并设定 origin 为默认主机
    git push -u origin master/main
    
  • 从远程服务器上克隆一个仓库到本地

    # 从 https://github.com/yuguoliusheng 上克隆 introduce.git 到本地 introduce 文件夹
    git clone https://github.com/yuguoliusheng/introduce.git introduce
    
  • 忽略某些文件或文件夹 有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。通常都是些自动生成的文件,像是日志或者编译过程中创建的等等。可以创建 .gitignore 文件来屏蔽这些

    # 此为注释 – 将被 Git 忽略
    *.a        # 忽略所有 .a 结尾的文件
    !lib.a     # 但 lib.a 除外
    /TODO      # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
    build/     # 忽略 build/ 目录下的所有文件
    doc/*.txt  # 会忽略 doc/notes.txt, 但不包括 doc/server/arch.txt
    
  • 查看提交的更新

    # 查看提交的日志,并显示最近两次提交更新区别
    git log -p -2
    

    选项说明
    -p 按补丁格式显示每个更新之间的差异。
    --stat 显示每次更新的文件修改统计信息。
    --shortstat 只显示 --stat 中最后的行数修改添加移除统计 。
    --pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。

  • 存储当前工作空间 储藏 stashing 可以获取你工作目录的中间状态——也就是你修改过的被追踪的文件和暂存的变更——并将它保存到一个未完结变更的堆栈中,随时可以重新应用。

    # 存储当前工作空间,以方便进行切换
    git stash
    # 此时工作空间所有文件的状态就干净了,可以切换到其他工作空间
    
    # 显示之前存储的工作空间
    git stash list
    
    # 重新启用存储的工作空间
    git stash apply stash@2
    
    # 移除存储的工作空间
    git stash drop stash@2
    
    # 从存储中创建分支
    git stash branch testchanges
    

参考文献

  1. Git Pro第二版
  2. 百度百科VCS
  3. 维基百科Git
  4. 本地版本控制系统RCS
  5. 维基百科之差分编码

`如果该文章对您产生了帮助,或者您对技术文章感兴趣,可以关注微信公众号: 技术茶话会, 能够第一时间收到相关的技术文章,谢谢!

技术茶话会`

本篇文章由一文多发平台ArtiPub自动发布