《走进git时代系列三》详解部分git思想及SVN/GIT命令对比解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 本文首先又一次强调了Git和SVN 版本管理系统思想上的差别, 举例说明了文件存储方式,Git分支到底是什么。 后半部分按照日常工作的场景,将使用SVN/GIT需要用到的命令进行了列举和对比。

主要是对git的特性做了个引导, 用户还是需要自己找资料系统的去学习。

  • 所以补充一篇分享,稍微详细一点解释一下svn和git的差异性, 以及日常工作的命令对比 。 帮助大家在学习git的道路上更加清晰。

目录


  1. 再谈SVN/GIT思想差别带来的影响

  2. 你需要知道的SVN/GIT命令操作


  • 首先还是要强调一下 : 在开始学习 Git 的时候,请不要尝试把各种概念和其他版本控制系统(SVN,P4等)相比拟,否则容易混淆每个操作的实际意义。

  • Git 在保存和处理各种信息的时候,虽然操作起来的命令形式非常相近,但它与其他版本控制系统的做法颇为不同。理解这些差异将有助于你准确地使用 Git 提供的各种工具。

文件存储方式不同带来的核心差异

  • 系列一的文章中已经提及过,并且给大家看过下面这两张图片,为什么还拿出来, 因为确实一定要强调,大多数其他系统(CVS,Subversion,Perforce,Bazaar 等等)只关心文件内容的具体差异。这类系统每次版本记录有哪些文件作了更新,以及都更新了哪些行的什么内容 。

  • _2016_03_02_5_15_56

  • 那带来的一个最大的问题就是 : SVN让你无论如何不能重构代码的树冲突

  • 树冲突,指的是由于目录(文件)树的改变,造成内容修改修改不能匹配在同一对象(目录/文件)上。 当一名开发人员移动、重命名、删除一个文件或文件夹,而另一名开发人员也对它们进行了移动、重命名、删除或者仅仅是修改时就会发生树冲突。

    • 例如:由于在一个分支中修改的目录和文件,在另外的分支出现了改名的操作。

    • 或因为两个分支同时增加了一个同名的目录,导致了树冲突。

  • 阿里巴巴内部的SCM同学花了巨大的精力去解决树冲突,还申请了一个专利……

  • 在业内,SVN的树冲突也是一直难以解决的问题,比如这个帖子,看完之后都觉得残忍: http://www.oschina.net/question/103087_12309


  • 再说Git : Git 并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一链接。

  • _2016_03_02_5_18_59

  • 好处:除了完美解决了树冲突以外, 后面会讲到的强大的分支概念也是源于这样的思想。


Git 另外两个特性

  • 时刻保持数据完整性

    • 在保存到 Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。所以如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。

    • Git 使用 SHA-1 算法计算数据的校验和,通过对文件的内容或目录的结构计算出一个 SHA-1 哈希值,作为指纹字符串。所有保存在 Git 数据库中的东西都是用此哈希值来作索引的,而不是靠文件名。

  • 多数操作仅添加数据

    • 这种高可靠性令我们的开发工作安心不少,尽管去做各种试验性的尝试好了,再怎样也不会弄丢数据。所以又被称作无线后悔药的版本管理系统。

再详说Git 分支

  • 有人把 Git 的分支模型称为“必杀技特性”,而正是因为它,将 Git 从版本控制系统家族里区分出来。

  • Git 的分支可谓是难以置信的轻量级,它的新建操作几乎可以在瞬间完成,并且在不同分支间切换起来也差不多一样快。 Why ?

  • 之前已经提过,Git 保存的不是文件差异或者变化量,而只是一系列文件快照。在 Git 中提交时,会保存一个提交(commit)对象,该对象包含一个指向暂存内容快照的指针,包含本次提交的作者等相关附属信息。

  • 举个栗子: 假设本地有3个文件,当使用git commit新建一个提交之前,Git 会先计算每一个子目录的校验和,然后在 Git 仓库中将这些目录保存为树(tree)对象。之后 Git 创建的提交对象,除了包含相关提交信息以外,还包含着指向这个树对象(项目根目录)的指针,如此它就可以在将来需要的时候,重现此次快照的内容了。

  • 现在git 仓库中有五个对象(如下图所示):三个表示文件快照内容的 blob 对象;一个记录着目录树内容及其中各个文件对应 blob 对象索引的 tree 对象;以及一个包含指向 tree 对象(根目录)的索引和其他提交信息元数据的 commit 对象。

  • _2016_03_02_5_22_32

  • 作些修改后再次提交,那么这次的提交对象会包含一个指向上次提交对象的指针(即下图中的 parent 对象)。两次提交后,仓库历史会变成下面的样子

  • _2016_03_02_5_23_08

  • 所以,Git 中的分支:其实本质上仅仅是个指向 commit 对象的可变指针。Git 会使用 master 作为分支的默认名字。

  • 在若干次提交后,你其实已经有了一个指向最后一次提交对象的 master 分支,它在每次提交的时候都会自动向前移动。

  • _2016_03_02_5_23_58


Git 分支的变化

  • 再举一个例子: 假设当前的代码分支情况如图所示,当前,我们在 master 分支工作,HEAD 指向着当前的 master 分支。

  • HEAD 文件是一个指向你当前所在分支的引用标识符。这样的引用标识符——它看起来并不像一个普通的引用——其实并不包含 SHA-1 值,而是一个指向另外一个引用的指针。

  • screenshot

  • 执行 git checkout testing 转换到 testing 分支。这时变化如下图,HEAD 指向了 testing 分支。

  • screenshot

  • 修改文件并做一次提交:

    • echo aaa >> test.txt
    • git commit -a -m 'made change'
  • 提交后发生的变化如下图所示,现在 testing 分支向前移动了一格,而 master 分支仍然指向原先 git checkout 时所在的 commit 对象。

  • screenshot

  • 使用git checkout master 命令回到 master 分支,效果如下图所示。

  • 这条命令做了两件事:它把 HEAD 指针移回到 master 分支,并把工作目录中的文件换成了 master 分支所指向的快照内容。也就是说,现在开始所做的改动,将始于本项目中一个较老的版本。它的主要作用是将 testing 分支里作出的修改暂时取消,这样你就可以向另一个方向进行开发。

  • screenshot


你需要知道的SVN/GIT命令操作

  • 强调:我是非常不推荐把SVN和GIT的命令做对比参考的,因为很多命令因为原理不同背后的操作是完全不一样的。 但是如果这个对svn 转git 操作学习有点点帮助,还是列出来吧 ~~
操作 GIT SVN
检出/复制/克隆 git clone svn checkout
提交 git commit svn commit
查看提交的详细记录 git show svn cat
确认状态 git status svn status
确认差异 git diff svn diff
确认记录 git log svn log
添加 git add svn add
移动 git mv svn mv
删除 git rm svn rm
取消修改 git checkout / git reset svn revert
创建分支 git branch svn copy
切换分支 git checkout svn switch
合并 git merge svn merge
创建标签 git tag svn copy
更新 git pull / git fetch svn update
反映到远端 git push svn commit
忽略档案目录 .gitignore .svnignore
  • 注意: 刚刚已经讲过,SVN的revert是用来取消修改,但Git的revert是用来消除提交。

  • 以及svn commit 会直接和中央仓库交互, svn branch/tag的构造是一样的,git的branch/tag是两回事。

下面开始对场景需要的命令逐一解释


设置与创建新仓库

  • git 的全局设置

    • git config --global user.name “your_name"
    • git config --global user.email “your@email.com"
  • 常用快捷键设置:

    • git config --global alias.st status
    • git config --global alias.co checkout
    • git config --global alias.br branch
    • git config --global alias.ci commit
    • git config --global color.ui true //打开颜色开关
  • 稍微有点意思的配置:

  • git config --global alias.clog "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset' --abbrev-commit --date=relative"

  • 配置后执行效果如下图:

  • _2016_03_02_5_51_51

  • 创建新仓库:git init 以创建新的 git 仓库。

  • 检出仓库:

    • git clone git@code.aliyun.com:group/project.git
    • git clone http://code.aliyun.com/group/project.git
    • svn checkout http://code.taobao.org/repos/repo

添加和提交

  • Git:

    • 你可以提出更改(把它们添加到暂存区),使用如下命令:
    • git add <filename>
    • git add .
    • 使用如下命令以实际提交改动:
    • git commit -m "代码提交信息"
    • 你的改动已经提交到了 HEAD,但是还没到你的远端仓库。
  • SVN:

    • 新增文件,svn也需要
    • svn add
    • 但是修改已有文件, 直接通过
    • svn commit -m “代码提交信息"
    • 就直接将变化和版本提交到了远端中心仓库。

Git revert 和 reset

  • 添加和提交这里要强调下git 缓存区的概念。 系列一文章里讲过 缓存区和 文件的三种状态的概念, 简单就像下面图中所示:

  • screenshot

  • 平常工作的正常流程是: 修改本地文件,状态从上一次commit 变为修改状态,通过git stash 或 git add 放到缓存区, 再通过git commit将修改进入版本库。 当然你也可以跳过缓存区,直接commit -a

  • 如果你把缓存区的文件又进行了编辑修改, 这个文件的内容就会一部分被缓存,当然可以通过git checkout -f,撤销第二次的修改,也可以git add 将第二次的修改也加入到缓存区内,再进行commit 。

  • 当然如果你想撤销你的修改,可以通过git reset 或 git revert ,但当你的commit已经push到远端,被别人pull了下来, 再reset push 的话,别人再pull 就会出现错误,因为这个commit 节点回退到了你本地的缓存区,不在版本系统内,会很麻烦。

  • 所以这种情况下需要使用 git revert ,它是撤销某次操作,此次操作之前和之后的commit和history都会保留,并且把这次撤销作为一次最新的提交。

  • 是提交一个新的版本,将需要revert的版本的内容再反向修改回去,版本会递增,不影响之前提交的内容,别人pull的时候不会出问题,这个很重要。

改动和推送

  • 你的改动现在已经在本地仓库的 HEAD 中了。执行如下命令以将这些改动提交到远端仓库:

    • git push origin master //可以把 master 换成你想要推送的任何分支。
  • 如果你还没有克隆现有仓库,并想将你的仓库连接到某个远程服务器,你可以使用如下命令添加:

    • git remote add origin git@code.aliyun.com:group/project.git
  • 如果要添加多个远程服务器 ,那么:

    • git remote add github git@github.com:group/project.git
  • 那么,在需要推送到另外的远端仓库则:

    • git push github master
  • SVN 没有推送的概念, svn commit 则直接将版本提交到中央仓库。

创建分支 — Git

  • 例如要做如图中的操作:

  • screenshot

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

    • git checkout -b feature_x
  • 切换回主分支:

    • git checkout master
  • 合并分支到主分支,再把新建的分支删掉:

    • git merge feature_x ; git branch -d feature_x

创建分支 — SVN

  • SVN中不存在本地创建分支,合并,推送的这些概念,所以当svn创建分支时,一般是这两种做法:

    • svn copy trunk branches/my-feature_x-branch

    • svn commit -m "Creating a branch"

  • 或者直接远程操作:

    • svn copy http://code.taobao.org/repos/test/trunk http://code.taobao.org/repos/test/branches/my-feature_x-branch -m "Creating a branch"
  • 当需要切换分支时,svn有类似的概念:

    • svn switch http://目录全路径 本地目录全路径
  • 这个命令会更新你的工作副本,映射到一个新的URL,其行为跟“svn update”很像,也会将服务器上文件与本地文件合并。

更新与合并 — Git

  • 要更新你的本地仓库,执行:

    • git pull origin <branch>
  • 这个命令会在你的工作目录中 获取(fetch) 并 合并(merge) 远端的改动。

  • 由于pull 会直接进行merge ,所以建议先进行git fetch -p ,获取远端的变化,再进行merge

  • 要合并其他分支到你的当前分支(例如 master),执行:

    • git merge <branch>
  • 在这两种情况下,git 都会尝试去自动合并改动。遗憾的是,这可能并非每次都成功,并可能出现冲突(conflicts)。 这时候就需要你修改这些文件来手动合并这些冲突(conflicts)。改完之后,你需要执行如下命令以将它们标记为合并成功:

    • git add <filename>
  • 在合并改动之前,你可以使用如下命令预览差异:

    • git diff <source_branch> <target_branch>
  • 合并区分fast-forward 和 no-fast-forward , 在系列1文章中已经讲到。

更新与合并 -- SVN

  • 需要更新时:

    • svn update
    • svn up
  • 当svn进行合并时一般的做法是:

    • svn merge -r m:n path
    • svn merge branchA branchB
  • 当出现冲突时:

    • svn revert file // 撤销修改
  • 当冲突解决后:

    • svn resolved //resolved命令除了删除冲突文件,还修正了一些记录在工作拷贝管理区域的记录数据

标签

  • 为软件发布创建标签是推荐的。你可以执行如下命令创建一个叫做 1.0.0 的标签:

    • git tag 1.0.0 1b2e1d63ff
  • 1b2e1d63ff 是你想要标记的提交 ID 的前 10 位字符。可以使用下列命令获取提交 ID:

    • git log
  • 你也可以使用少一点的提交 ID 前几位,只要它的指向具有唯一性。 但我们经常会在最新的commit上打标签,则直接运行git tag 1.0.0 即可。

  • SVN的标签和他的分支是同样的概念, 一样是通过svn copy , 例如:

    • svn copy trunk tags/1.0.0
    • svn commit -m "Creating a tag"

替换本地改动 (reset和revert 刚才已经讲过)

  • 假如你操作失误(当然,这最好永远不要发生),你可以使用如下命令替换掉本地改动:
    • git checkout -- <filename>
  • 此命令会使用 HEAD 中的最新内容替换掉你的工作目录中的文件。已添加到暂存区的改动以及新文件都不会受到影响。

  • 假如你想丢弃你在本地的所有改动与提交,可以到服务器上获取最新的版本历史,并将你本地主分支指向它:

    • git fetch origin
    • git reset --hard origin/master

Git需要特别知道的

  • 春节前 info 公众号推送了一篇 你需要知道的12个Git高级命令

  • 这里面其实最关键的就是:

    • 缓存区,Stash 的运用,

      • reset 和 revert的差别
    • merge,rebase, check-pick 的差别和运用

SVN需要特别知道的

  • SVN 相比git使用上来说, 只有一点(我认为也不是版本管理系统应该保护的点) 优势,就是悲观锁。

  • 当commit 时或者直接通过lock/unlock 命令对某一个文件加锁:

    • svn lock -m "LockMessage" [--force] PATH
    • svn unlock PATH
  • 如果这个文件是二进制文件或者非代码文件(PPT什么的), 这个锁能够减少多人同时修改一个文件,而这个文件又无法diff的代价。

  • 如果多人同时修改并提交二进制文件, 对版本库是有较大的成本的,但是二进制文件不应该进入到代码管理系统内。


写在最后

  • 其实本文很多内容都是直接从《pro git》 这本书里摘的, 愿意系统学习的同学还是要看这本书 。

  • 其他内容感觉日常基本工作也就这些操作了, git 操作方面 也告一段落, 以后再写就是协作方面的了。

目录
相关文章
|
11天前
|
运维 Shell 数据库
Python执行Shell命令并获取结果:深入解析与实战
通过以上内容,开发者可以在实际项目中灵活应用Python执行Shell命令,实现各种自动化任务,提高开发和运维效率。
43 20
|
10天前
|
网络安全 开发工具 git
mac git clone命令提示git@gitee.com: Permission denied (publickey).问题修复
mac git clone命令拉取gitee上项目代码时提示密钥问题
|
5天前
|
网络协议 Unix Linux
深入解析:Linux网络配置工具ifconfig与ip命令的全面对比
虽然 `ifconfig`作为一个经典的网络配置工具,简单易用,但其功能已经不能满足现代网络配置的需求。相比之下,`ip`命令不仅功能全面,而且提供了一致且简洁的语法,适用于各种网络配置场景。因此,在实际使用中,推荐逐步过渡到 `ip`命令,以更好地适应现代网络管理需求。
21 11
|
3月前
|
开发工具 git
git 常用命令
这些只是 Git 命令的一部分,Git 还有许多其他命令和选项,可根据具体需求进行深入学习和使用。熟练掌握这些命令能够帮助你更高效地管理代码版本和协作开发。
|
9天前
|
Java 网络安全 开发工具
Git进阶笔记系列(01)Git核心架构原理 | 常用命令实战集合
通过本文,读者可以深入了解Git的核心概念和实际操作技巧,提升版本管理能力。
|
2月前
|
存储 SpringCloudAlibaba Java
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论。
【SpringCloud Alibaba系列】一文全面解析Zookeeper安装、常用命令、JavaAPI操作、Watch事件监听、分布式锁、集群搭建、核心理论
|
2月前
|
机器学习/深度学习 Shell 网络安全
【Git】Git 命令参考手册
Git 命令参考手册的扩展部分,包含了从基础操作到高级功能的全面讲解。
63 3
|
3月前
|
缓存 Java Shell
[Git]入门及其常用命令
本文介绍了 Git 的基本概念和常用命令,包括配置、分支管理、日志查看、版本回退等。特别讲解了如何部分拉取代码、暂存代码、删除日志等特殊需求的操作。通过实例和图解,帮助读者更好地理解和使用 Git。文章强调了 Git 的细节和注意事项,适合初学者和有一定基础的开发者参考。
69 1
[Git]入门及其常用命令
|
4月前
|
开发工具 git
git学习四:常用命令总结,包括创建基本命令,分支操作,合并命令,压缩命令,回溯历史命令,拉取命令
这篇文章是关于Git常用命令的总结,包括初始化配置、基本提交、分支操作、合并、压缩历史、推送和拉取远程仓库等操作的详细说明。
165 1
git学习四:常用命令总结,包括创建基本命令,分支操作,合并命令,压缩命令,回溯历史命令,拉取命令
|
4月前
|
网络协议 开发工具 C语言
Jetson错误(二):wget命令提示无法解析主机地址的问题解决
对于解决在NVIDIA Jetson平台上使用wget命令时出现的无法解析主机地址的问题,提供了两种解决方法:一种是临时修改DNS服务器为Google的公共DNS,另一种是永久修改DNS设置。
192 5

推荐镜像

更多