1

Git introduction

标签(空格分隔): 版本控制 Git

本文是个人对Git版本控制系统的学习,Git相关知识的学习。介绍Git的基本知识,常用操作等等。


版本控制系统

VCS:Version Control System
版本控制系统是记录文件的内容变化,以便在任何时候可以查看文件在特定版本修订情况的系统。它可以对任何文件进行版本控制,不管是代码文件、图片文件、文档文件等等。所以有了VCS之后我们可以将某个文件回退到之前的某一个状态,甚至可以将整个项目都回退到过去某个时间点的状态。
当然通过VCS可以比较文件具体的变化细节:

    + var a = 5                                          - var a = 3
-        console.log(JSON.stringify(data))
-        console.log('isVcardUsing====', self.freeUse)
-        console.log(typeof self.freeUse)
+        // console.log(JSON.stringify(data))
+        // console.log('isVcardUsing====', self.freeUse)
+        // console.log(typeof self.freeUse)

这样在出现一些bugs的时候我们可以去分析出最后是谁修改了哪个地方,从而发现导致bugs的原因,从而可以快速修复问题。通过VCS的目的并不是说要去追踪是谁导致出现问题的人,而是帮助我们快速的去解决问题。
不过目前很多公司有着一套版本发布或持续集成系统,如果发布上线之后发现有问题出现,可以快速的回退到上一个指定没有问题的版本,然后在通过分析导致bugs的原因重新上线。
另外假设再开发过程中不小心把项目中的文件改的改删的删,我们也可以轻松的恢复到原来的状态。

版本控制系统历史分类:

  1. 本地版本控制系统:rcs
  2. 集中化版本控制系统CVCS:CVS,subversion……
  3. 分布式版本控制系统DVCS:Git,Mercurial,Bazaar……

Git历史

Linux内核开源项目有很多的参与者,但是绝大多数的Linux内核维护工作都花在提交补丁和保存归档的繁琐事务上面(1991-2002),到 2002 年,整个项目组开始启用分布式版本控制系统 BitKeeper 来管理和维护代码。到了2005年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了免费使用 BitKeeper 的权力。自此Git分布式版本控制系统就诞生了。

Git当初设定的目标:

  • 速度
  • 简单的设计
  • 对非线性开发模式的强力支持(允许上千个并行开发的分支)
  • 完全分布式
  • 有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)

Git基础

理解Git的思想和基本工作原理,用起来就会知其所以然,游刃有余。

  • Git的原理是直接记录快照,并非差异比较:Git并不保存文件前后变化的差异数据,实际上是把变化的文件做快照后,记录在一个微型的文件系统中;
  • Git所有的操作都是本地执行;
  • 时刻保持数据的完整性:在保存到Git之前,所有的数据都会进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。
  • Git使用SHA-1算法计算数据的校验和,此SHA-1 哈希值作为指纹字符串,该字串由 40 个十六进制字符(0-9 及 a-f)组成;
  • 多数操作仅添加数据:常用的 Git 操作大多仅仅是把数据添加到数据库;
  • 文件的三种状态
    1.已修改 modified 表示修改了某个文件,但还没有提交保存
    2.已暂存 staged 表示把已修改的文件放在下次提交时要保存的清单中
    3.已提交 commited 表示该文件已经被安全地保存在本地数据库中了

Git管理项目时,文件流的三个工作区域:Git工作目录,暂存区域,本地仓库(Git目录.git)。

clipboard.png


Git初始设置

  • git config --system --list (/etc/gitconfig)
  • git config --global --list (~/.gitconfig)
  • git config (--local) --list (当前项目的 git 目录中的配置文件(也就是工作目录中的 .git/config 文件))
  • git config --global user.name 'username'
  • git config --global user.email 'useremail'

如果用了 --global 选项,那么更改的配置文件就是位于你系统用户主目录下的那个(~/.gitconfig),以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 --global 选项重新配置即可,新的设定保存在当前项目的 .git/config 文件里。

初始Git respository

有两种取得 Git 项目仓库的方法:
第一种是在现存的目录下,通过导入所有文件来创建新的 Git 仓库;
第二种是从已有的 Git 仓库克隆出一个新的镜像仓库来;

第一种:

  • git init 当前目录创建git仓库
  • git init name 创建目录为name的git仓库

第二种

  • git clone https://github.com/donnyqi/Html.git 拷贝远程git仓库,目录为Html
  • git clone https://github.com/donnyqi/Html.git HtmlTest 拷贝远程git仓库,目录为自定义的HtmlTest

记录每次更新到仓库

文件的状态变化周期:
工作目录下的所有文件都不外乎两种状态:已跟踪 、 未跟踪
已跟踪的文件是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是未更新,已修改或者已放入暂存区。而所有其他文件都属于未跟踪文件。它们既没有上次更新时的快照,也不在当前的暂存区域。初次克隆某个仓库时,工作目录中的所有文件都属于已跟踪文件,且状态为未修改。

文件状态变化周期:
图片描述

状态的提示语:

  • untracked Untracked files,未跟踪文件
  • unmodified 未做任何修改
  • modified Changes not staged for commit,已修改文件
  • staged Changes to be committed,已暂存文件

基本的 Git 工作流程如下:

1.在工作目录新加了文件--->Untracked files(工作目录新加文件)
2.在工作目录中修改某些文件--->modified:Changes not staged for commit(工作目录修改了文件)
3.对新加的文件、修改后的文件进行快照(git add操作),然后保存到暂存区域--->staged:Changes to be committed(修改的文件保存到暂存区域)
4.提交更新(git commit操作),将保存在暂存区域的文件快照永久转储到Git目录中--->commited(暂存区域的文件提交到本地仓库)
bogon:static vivo$ git status
On branch branch_point_20180426_v3_2_1
Your branch is up-to-date with 'origin/branch_point_20180426_v3_2_1'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   src/api/test.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   src/api/index.js
        modified:   src/router/index.js

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        src/views/Test/

Git 基础命令操作

  • git status 检查当前文件状态
  • git log 查看提交历史,git log具有很多格式化参数,建议使用图形化工具gitk,相当于git log命令的可视化版本
  • git add filename || 目录 跟踪新文件,已跟踪文件&未跟踪文件放到暂存区(译注:其实 git add 的潜台词就是把目标文件快照放入暂存区域,也就是 add file into staged area,同时未曾跟踪过的文件标记为需要跟踪。这样就好理解后续 add 操作的实际意义了。)
  • git add . 跟踪所有文件
  • git diff (--cached || --staged) 查看已暂存文件或未暂存文件与暂存区域快照之间的更新
  • git diff 查看工作目录中当前未暂存的文件与暂存区域快照直接的差异,也就是修改之后还没有暂存起来的变化内容。如果未暂存的文件全部都add到暂存区域,git diff是没有变化的;如果新加的文件(Untracked files),git diff也是没有变化的
  • git diff --cached || --staged 当前工作目录中已暂存文件与上次提交时的快照之间的差异 或者通过开发工具比较
  • git commit 进入Vim输入本次提交的说明(默认的提交消息包含最后一次运行 git status 的输出)->i->input commit message->esc-> : ->wq->enter
  • git commit -v 进入Vim输入本次提交的说明(将修改差异的每一行都包含到注释中来)
  • git commit -m "commit message" 直接提交说明
  • git commit -a -m "commit message" Git会自动把所有已经跟踪过的文件暂存起来一并提交直接加到staged区域,从而跳过 git add 步骤,但是不能添加untracked文件(-a : 此操作跳过了git add 操作)
  • git commit -a 进入Vim输入本次提交的说明,把已经跟踪过的文件暂存到暂存区域,但是不能添加untracked文件(-a : 此操作跳过了git add 操作)
  • git commit --amend 进入Vim修改最后一次提交的提交信息。此操作提交的文件快照和之前是一样的,只是重新编辑提交说明并覆盖。如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行 --amend 提交:

      git commit -m "message"
      git add 'filename'
      git commit --amend
      上面三条命令最终只会产生一个提交,以第二个提交命令的提交信息为准
  • git mv old_filename new_filename 重命名文件
  • git mv path/old_filename path/new_filename 移动文件并且重命名文件
  • git reset (HEAD) filename 取消已经暂存的文件(放弃工作目录中当前暂存的文件到未暂存或未追踪)
  • git checkout filename 取消对文件的修改(放弃工作目录中当前修改的文件),注:但是取消对文件的修改操作执行时候所有对文件的修改就都没有了,是有风险的,所有在执行此操作前确保真的不再需要保留刚才的修改

注:如果在新跟踪了某个untracked文件或者在跟踪了某个已修改后的文件,添加到staged区域(Changes to be committed)之后,再次对该跟踪的文件进行了修改,这个时候如果现在提交,那么提交的是之前add的版本,而非当前工作目录中的版本。所以,运行了 git add 之后又作了修订的文件,需要重新运行 git add 把最新版本重新暂存起来

Git 远程仓库的使用

  • git remote 每个远程库的简短名字
  • git remote -v 显示对于的克隆地址
  • git remote add shortname url 添加远程仓库:git remote add pb git://github.com/paulboone/ticgit.git
  • git fetch remote-name 从远程仓库抓取数据
  • git push remote-name branch-name 本地仓库中的数据推送到远程仓库
  • git remote show remote-name 查看远程仓库信息
  • git remote rename old-remote-name new-remote-name 修改某个远程仓库在本地的简称
  • git remote rm remote-name 移除对应的远端仓库

Git标签 tag

Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。而含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。

  • git tag 列出所有的标签
  • git tag -l v1.7* 列出所有包含v1.7标签
  • git tag tag-name 创建一个轻量级tag
  • git tag -a tag-name -m annotated 创建一个含附注的标签(-m 选项则指定了对应的标签说明,Git 会将此说明一同保存在标签对象中。如果没有给出该选项,Git 就会启动文本编辑软件供你输入标签说明。类似commit -m)
  • git tag -a tag-name commitid 后期加注标签,在后期对早先的某次提交加注标签
  • git show tag-name 查看相应标签的版本信息,并连同显示打标签时的提交对象
  • git tag -d tag-name 删除tag
  • git push origin tagname 推送tag到远端服务器
  • git push origin --tags 推送所有本地所有tags到远端服务器

Git 分支

Git 的分支可谓是难以置信的轻量级,它的新建操作几乎可以在瞬间完成,并且在不同分支间切换起来也差不多一样快。和许多其他版本控制系统不同,Git 鼓励在工作流程中频繁使用分支与合并,哪怕一天之内进行许多次都没有关系。理解分支的概念并熟练运用后,你才会意识到为什么 Git 是一个如此强大而独特的工具,并从此真正改变你的开发方式。
为了理解 Git 分支的实现方式,我们需要回顾一下 Git 是如何储存数据的:Git 保存的不是文件差异或者变化量,而只是一系列文件快照。
**Git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针(分支其实就是从某个提交对象往回看的历史)。**
Git 是如何知道你当前在哪个分支上工作的呢?Git保存着一个名为 HEAD 的特别指针,它是一个指向你正在工作中的本地分支的指针(注:将 HEAD 想象为当前分支的别名。

这和大多数版本控制系统形成了鲜明对比,它们管理分支大多采取备份所有项目文件到特定目录的方式,所以根据项目文件数量和大小不同,可能花费的时间也会有相当大的差别,快则几秒,慢则数分钟。而 Git 的实现与项目复杂度无关,它永远可以在几毫秒的时间内完成分支的创建和切换。同时,因为每次提交时都记录了祖先信息(译注:即 parent 对象),将来要合并分支时,寻找恰当的合并基础(译注:即共同祖先)的工作其实已经自然而然地摆在那里了,所以实现起来非常容易。Git 鼓励开发者频繁使用分支,正是因为有着这些特性作保障。

  • git branch branch-name 新建一个分支
  • git checkout branch-name 切换到输入的分支上,HEAD就会指向checkout的分支
  • git checkout -b branch-name 新建一个分支并且切换到该分支上,相当于上面两步的合并
  • git merge branch-name 将输入的分支合并到当前分支
  • git branch 列出git仓库本地的所有分支
  • git branch -a 列出git仓库远程与本地所有分支
  • git branch -v 查看各个分支最后一个提交对象的信息
  • git branch --merge 从分支清单中筛选出你已经与当前分支合并的分支
  • git branch --no-merge 从分支清单中筛选出你尚未与当前分支合并的分支
  • git branch -d branch-name 删除分支
  • git branch -D branch-name 强制删除分支

远程分支

远程分支(remote branch)是对远程仓库中的分支的索引。它们是一些无法移动的本地分支;只有在 Git 进行网络交互时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置。
我们用 远程仓库名/分支名 这样的形式表示远程分支。

  • git fetch origin 同步远程服务器上的数据到本地,origin为远程仓库的简短名字(默认为origin)
  • git push 远程仓库名 分支名 推送分支
  • git push 远程仓库名 本地分支名:远程分支名

    git push origin master
    git push origin master:master
    git push origin master:refs/heads/master
    git push origin refs/heads/master:refs/heads/master
    上面四条命令的效果是一样的
    
    你也可以把本地分支推送到某个命名不同的远程分支:若想把远程分支叫作 anotherbranch,可以用:
    git push origin `branch-name`:`anotherbranch-name` 来推送数据
  • git push origin :远程分支名 删除远程分支
  • git branch -r -d origin/v1.0 删除远程分支

跟踪远程分支

  • git checkout --track 远程仓库名/远程分支名
  • git checkout 远程分支名
  • git checkout -b 分支名 远程仓库名/远程分支名

    git checkout --track origin/test
    git checkout test
    上面两个命令效果相同
    要为本地分支设定不同于远程分支的名字:git checkout -b test-alias origin/test

分支的衍合

把一个分支中的修改整合到另一个分支的办法有两种:merge(合并) 和 rebase(衍合)
最容易的整合分支的方法是 merge 命令,它会把两个分支最新的快照(C3 和 C4)以及二者最新的共同祖先(C2)进行三方合并,合并的结果是产生一个新的提交对象(C5)
rebase 命令:把在一个分支里提交的改变移到另一个分支里重放一遍

  • git rebase master
  • git rebase --continue 处理完冲突继续合并
  • git rebase --skip 跳过
  • git rebase --abort 取消合并

衍合的原理是回到两个分支最近的共同祖先,根据当前分支(也就是要进行衍合的分支)后续的历次提交对象(这里只有一个 C3),生成一系列文件补丁,然后以基底分支(也就是主干分支 master)最后一个提交对象(C4)为新的出发点,逐个应用之前准备好的补丁文件,最后会生成一个新的合并提交对象(C3'),从而改写 experiment 的提交历史,使它成为 master 分支的直接下游,如下图所示:
图片描述
其实衍合和普通的三方合并(merge),对应的快照内容一模一样了。虽然最后整合得到的结果没有任何区别,但衍合能产生一个更为整洁的提交历史。如果视察一个衍合过的分支的历史记录,看起来会更清楚:仿佛所有修改都是在一根线上先后进行的,尽管实际上它们原本是同时并行发生的。
请注意,合并结果中最后一次提交所指向的快照,无论是通过衍合,还是三方合并,都会得到相同的快照内容,只不过提交历史不同罢了。衍合是按照每行的修改次序重演一遍修改,而合并是把最终结果合在一起。


donny
11 声望0 粉丝

下一篇 »
Font Boosting