iOS开发中的源代码管理工具

<h4>源代码管理工具的起源</h4>

  • <h5>为什么会出现源代码管理工具?</h5>
    • 为了解决在软件开发过程中,由源代码引发的各种蛋疼繁琐问题
  • <h5>源代码会引发哪些问题?</h5>
    • 无法后悔:做错了一个操作后,没有后悔药可吃
    • 版本备份:费空间、费时间
    • 版本混乱:因版本备份过多造成混乱,难于找回正确的想要的版本
    • 代码冲突:多人操作同一文件(团队开发中常见问题)
    • 权限控制:无法对源代码进行精确的权限控制
    • 追究责任:出现了严重的BUG,无法得知是谁干的

<h4>源代码管理工具的作用</h4>

  • 能追踪一个项目从诞生一直到定案的过程
  • 记录一个项目的多有内容变化
  • 方便地查阅特定版本的修订情况
  • ... ...

<h4>常见的源代码管理工具</h4>

  • <h5>CVS</h5>
    • 开启版本控制之门
    • 1990年诞生,“远古时代”的主流源代码管理工具
  • <h5>SVN</h5>
    • 全称是Subversion,集中式版本控制之王者
    • 是CVS的接班人,速度比CVS快,功能比CVS多且强大
    • 在国内使用率非常高(70%~90%)
  • <h5>GIT</h5>
    • 一款伟大的分布式源代码管理工具
    • 目前被越来越多的开源项目使用
    • 不过在国内企业尚未大规模普及

<h4>SVN</h4>

  • <h5>SVN基本操作</h5>


    SVN的基本操作.png
    • $ svn checkout:将服务器代码完整的下载到本地
    • $ svn commit:将本地修改的内容提交到服务器
    • $ svn update:将服务器最新的代码下载到本地
    • *注:Checkout只需要做一次!
  • 提示:
    • 每天下班前:commit一次“可运行版本代码”
    • 每天上班前:update前一天所有代码
  • <h5>SVN官方基本操作</h5>


    svn官方基本操作示意图.png
  • <h5>SVN使用环境</h5>
  • 想要使用SVN管理源代码,必须得有2套环境
    • <b>服务器</b>
      • 用于存储客户端上传的源代码
      • 可以在Windows上安装Visual SVN Server
      • 大部分情况下,公司的开发人员不必亲自搭建SVN服务器
    • <b>客户端</b>
      • 上传本地的源代码到服务器,或者更新服务器的代码到本地,保持同步
      • 可以在Mac上使用命令行、Versions、Cornerstone、Xcode
      • 开发人员就属于客户端这个角色

  • <h5>SVN客户端命令行演示</h5>
    一、初始化项目
    • 1.项目经理将服务器的已有的内容下载到本地(演示)
      • $ svn checkout 服务器地址 --username=xxx --password=xxx
    • 2.项目经理初始化项目(演示)
      • $ touch main.m:创建main.m
      • $ svn add main.m:将main.m添加到svn的管理之下
      • $ svn commit -m "xxx":将main.m上传到服务器
    • 3.查看文件状态(查看文件是否在svn的管理下,或者是否进行了修改而没有提交)
      • $ svn status
状态说明:描述文件被添加、删除或其他修改
' '   没有修改
'A'   该文件已经添加到svn的管理之下,但是该文件在本地,并未提交到服务器
'C'   冲突
'D'   被删除
'I'   被忽略
'M'   被修改
'R'   被替换
'X'   外部定义创建的版本目录
'?'   文件没有被添加到本地版本库中,不在svn的管理之下
'!'   文件丢失或者不完整(不识别该文件)
'~'   受控文件被其他文件阻隔
'U'   更新最新的代码到本地(本地有文件的情况下)
'G'   产生冲突后,更新操作去解决冲突,相当于进行合并
  • 4.张三加入开发
    • 1>将服务器所有的内容下载到本地
      $ svn checkout 服务器地址 --username=xxx --password=xxx

    • 2>开始开发
      $ touch person.h person.m:创建person类
      $ svn commit -m "创建了person类"

    • 3>经理更新代码
      $ svn update:更新服务器最新的代码(如果该文件在本地不存在,则下载,如果本地存在,则更新)

  • 5.命令行的简写
    • svn checkout -> svn co
    • svn status -> svn st
    • svn commit -> svn ci
    • svn update -> svn up
  • 6.版本回退
    $ svn revert 文件名(person.h):将本地新增的内容(没有提交到服务器),删除
    $ svn update -r版本号:先回退到某个版本,观察下,该版本是否是不想要的那个版本,示范:svn update -r6
    $ svn update:更新到最新的版本
    $ svn merge -r版本号(hight):版本号(low) 文件名:示范:svn merge -r7:6 person.h(本地)
    *注意:如果本地版本号低于服务器版本号,那么不能提交
    $ svn commit -m "回退到版本x"(服务器)
  • 7.删除文件
    $ svn remove 文件名:示范:svn remove person.m,svn remove -> svn rm(缩写)
  • 8.查看版本信息
    $ svn update:更新服务器最新的内容
    $ svn log:查看版本信息
  • 9.公司常用的命令
    $ svn update:更新
    $ svn commit -m "注释":将本地的代码提交到服务器

二、李四加入开发(新员工)
1>需要想项目经理要一些东西

  • 和项目经理要服务器地址以及账号地址
  • 需求文档:有什么需求,做什么样的功能
  • 接口文档:详细的记录服务器所有的功能
  • 效果图:界面到底长什么样子
    2>将服务器已有的内容下载到本地
  • $ svn checkout 服务器地址 --username=xxx password=xxx
    3>代码冲突(out of date:过期/本地版本号低于服务器的版本)
  • (df) diff-full:在命令行中展示所有的不同
  • (e) edit:在命令行中来编辑冲突
  • (mc) mine-conflict:用我本地的代码来覆盖服务器的代码
  • (tc) theirs-conflict:用服务器的代码来覆盖我的代码
  • (p) postpone:延迟解决冲突,展示所有冲突的文件,手动解决冲突 -> $ svn resolved 文件名(person.h)

*注意:如何避免冲突

  • 1>尽量在修改文件之前,先update
  • 2>如果修改公共文件,最好跟同事说一声,让它先别修改,修改完之后,让他更新>>

  • <h5>SVN图形化界面工具</h5>
  • 1.项目经理初始化项目
    • 1>项目经理将服务器已有的内容下载到本地
      • 记住format的选择 -> 1.7(最高)
    • 2>需要忽略的文件
      • xcuserdata
        • xcode会默认记录之前停留文件,下次打开依然停留在该文件,这个不需要共享
        • xcode会默认记录之前目录的打开状况,同事不需要共享
        • 断点信息,不需要进行共享
  • 2.在xcode中使用svn的注意点
    • 1>如果使用到静态库需要特别注意,必须使用命令行将静态库添加到svn的管理之下
    • 2>如果使用到了storyboard也需要特别注意
      • 如果能使用xib,尽量使用xib
      • 如果在项目当中使用到了storyboard,尽量保证只有一个人在操作storyboard
    • 3>checkout的方式
      • 使用命令行
      • 使用cornerstone(图形化界面工具)
      • Xcode
    • 4>公司开发技巧(避免冲突)
      • 尽量写一些代码就提交到服务器,实时跟服务器的代码保持同步

  • <h5>目录规范</h5>
  • 规范项目的svn目录结构一般有3个文件夹
    • trunk:主干,当前开发项目的主目录
    • branches:分支目录,添加非主线功能时使用,开发测试之后,可以合并到主干项目中
    • tags:标记目录,通常作为重大的版本的备份

<h4>Git</h4>

  • <h5>什么是git?</h5>
    • git是一款开源的分布式版本控制工具
    • 在世界上所有的分布式版本控制工具中,git是最快、最简单、最流行的
  • <h5>集中式版本控制</h5>


    集中式版本控制.png
  • <h5>分布式版本控制</h5>


    分布式版本控制.png
  • <h5>git和svn的简单对比</h5>
    • 速度:在很多情况下,git的速度远远比svn快
    • 结构:svn是集中式管理,git是分布式管理
    • 其他:
      • svn使用分支比较笨拙,git可以轻松拥有无限分枝
      • svn必须联网才能正常工作,git支持本地版本控制工作
      • 就版本的svn会在每一个目录放置一个.svn,git只会在根目录拥有一个.git
  • <h5>svn的工作流程</h5>


    SVN的工作流程.png

  • 分布式和集中式的最大区别在于:在分布式下
    • 开发者可以本地提交
    • 每个开发者机器上都有一个服务器的数据库

  • <h5>git的工作流程</h5>


    git的工作流程.png
  • <h5>git的使用</h5>
  • 跟svn一样,你可以通过命令行敲指令或者图像界面客户端使用git
  • 在Mac上,比较好用的git图形界面客户端有
  • 几个专用名词的译名:
    • Workspace : 工作区
    • Index/Stage : 暂存区
    • Repository : 本地仓库
    • Remote : 远程仓库
  • <h5>Git的工作原理</h5>
  • 如果想了解git的工作原理,有几个核心概念必须知道
    • 工作区(Working Directory):仓库文件夹里除了.git目录以外的内容(项目所在的文件目录)
    • 版本库(Repository):工作区又一个隐藏目录文件.git目录(可通过命令 ls -ah 查看隐藏文件)这就是Git的版本库,用于存储记录版本信息
    • 暂缓区(stage)
    • 分支(master):git自动创建的第一个分支
    • HEAD指针:用于指向当前分枝
  • <h5>Git的命令行演示</h5>
  • 0.安装Git
    • $ brew install git :Mac
    • $ sudo apt-get install git-core or $ sudo apt-get install git :Ubuntu or some linux OS


  • 1.如果使用Git,必须给Git配置用户名和邮箱
    • 给当前的git仓库配置用户名和邮箱
    • $ git config user.name "Vincent"
    • $ git config user.email "vn_vincent@outlook.com"
    • 给git配置全局的用户名和邮箱
    • $ git config --global user.name "Vincent"
    • $ git config --global user.email "vn_vincent@outlook.com"


  • 2.新建代码库
    • $ mkdir demo:建立目录
    • $ cd demo:进入demo目录
    • $ git init :创建一个本地仓库
    • $ git remote add origin gitAddress :本地库“关联”远程库
    • $ Git remote remove origin :取消本地目录下关联的远程库

  • $ git clone gitAddress :从远程仓库克隆项目代码
  • $ git clone -b C gitAddress :从远程仓库克隆“指定分支C”上的代码
  • $ git pull origin master:把远端“origin”节点的“master”拉回本机且进行合并


  • 3.初始化项目
    • $ touch main.m:创建了main.m
    • $ git add main.m:将main.m添加到暂缓区(staging area)
    • $ git commit -m "初始化项目":将在暂缓区的所有内容提交到本地版本库,并清空暂缓区
    • $ git push -u origin master


  • 4.增加/删除文件
    • $ git add [file] :将制定的文件添加到暂存区
    • $ git add . :添加当前目录下的所有文件
    • $ git add -p :添加每个变化前,都会要求确认;对于同一个文件的多处变化,可以实现分次提交
    • $ git add -u :暂存修改和删除的文件,不包括新增的文件
    • $ git add -A :暂存所有的文件,包括新增的,修改和删除的文件。
    • $ git rm [file1] [file2] ... :删除工作区为难,并且将这次删除放入暂存区
    • $ git rm -cached [file1] [file2] :(说法1: 删除本地仓库文件,但不会删除文件);(说法2: 停止追踪指定文件,但该文件回保留在工作区)
    • $ git mv [file-original] [file-renamed] :改名文件,并且将这个改名放入暂存区
      *注意:添加的文件或者修改的文件都要通过add命令将该文件添加到暂缓区


  • 5.代码提交
    • $ git commit -m "first commit" :提交暂存区刀仓库区
    • $ git commit [file1] [file2] ... -m [message] :提交暂存区的指定文件到仓库区
    • $ git commit -a :提交工作区自上次commit之后的变化,直接到仓库区
    • $ git commit -am:自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过$ git add步骤,参数 -am 也可写成 -a -m
    • $ git commit -v :提交时显示所有diff(扩展)消息
    • $ git commit -p:commit文件的一部分,适合工作量比较大的情况。之后,git会对这块修改弹出一个提示,询问你是否stage,按y/n来选择是否commit这块修改,?可以查看其他操作的说明
    • $ git commit --amend:最终只会有一次提交,第二次提交将代替第一次提交的结果。其使用场景:适用于提交完发现落掉几个文件没添加,或者提交信息写错的情况
    • $ git commit --amend [file1] [file2] ... :重做上一次commit,并包括指定文件的新变化
    • $ git commit --amend -m [message] :使用一次新的commit,代替上一次提交;如果代码没有任何新改变,则用来改写上一次commit的提交信息
    • $ git commit --amend --reset-author:在上一次commit之后想重新更新一下时间。amend实际上修改了上一个commit。所以如果已经push了上一个commit,请尽量不要amend。如果一定要amend已经push了的commit,请确保这个commit所在的branch只有你一个人在使用(负责会给其他人带来灾难),然后在amend之后使用 $ git push -force。只要多加小心,该命令貌似没什么卵用


  • 6.分支
    • $ git branch A :创建分支
    • $ git checkout -b A :创建分支A,并且切换到A分支上
    • $ git checkout B :切换到B分支上
    • $ git branch -a :查看所有本地分支和远程分支
    • $ git merge B :合并指定分支B到当前分支
    • $ git branch -d B :删除分支
    • $ git push origin --delete B :删除远程分支B
    • $ git branch -dr [remote / branch] :也是删除远程分支
    • $ git push origin 本地分支A : 远程分支A将本地分支push到远程分支,并且命名远程分支为A
    • $ git fetch origin :同步远程仓库
    • $ git push -u origin master :关联后,使用该命令第一次推动master分支的所有内容,后续在推送的时候就可以省略后面三个参数,其中u代表上游(upstream)的意思
    • $ git push --set-upstream origin A:git提示使用此命令


  • 7.撤销
    • $ git checkout --文件 :恢复暂存区的指定文件到工作区(丢弃工作区的修改,包括修改后还没有放到暂存区增加到暂换区后又作了修改两种情况。总之,让该文件会到最后一次$ git commit$ git add之后的状况。注意:没有-,就变成了切换分支的命令了

    • 在还没有add的时候
    • $ git checkout 那个路径下的文件夹或者文件名 :(就能撤销未添加的修改)
    • 如果已经add过的文件:
    • $ git reset HEAD 那个路径下的文件夹或者文件名 :(就能回到未添加的状态),然后再用$ git checkout [file]方法撤销修改
  • 注意:如果你把某个文件删除了,或者是新添加的文件,都不会在恢复或者去掉,但是会把文件中的添加的代码去掉

  • 如果已经commit过的文件:
  • $ git reset -hard HEAD^ :那么如果要回退到上上个版本只需把HEAD^改成HEAD^^以此类推。那如果要回退到前100个版本的话,使用上面的方法肯定不方便,我们可以使用下面的简单指令操作:$ git reset -hard HEAD~100 即可
  • 不过还有一种比较实用的方法:
  • $ git reset -hard [你要回退的版本号] :这样就能直接回退到你置顶的那一次提交

  • $ git reflog :可获取到每次提交的版本号

  • 已经push过的
  • 粗暴的方法:(只需两行命令)
  • $ git reset -hard 1243ad3(commit唯一标识) 或者$ git checkout (commit唯一标识 前7位)
  • $ git push origin HEAD -force :(强行push)

  • +7.改写提交
    本地端的歷史記錄狀態.png

    $git rebase -i HEAD~~:使用rebase -i命令选择要修改的提交
  • 预设文字编辑器会开启从HEAD到HEAD~~的提交,如下图显示内容
pick 9a54fd4 添加commit的說明
pick 0d4a808 添加pull的說明

# Rebase 326fc9f..0d4a808 onto d286baa
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

将第二行的“pick”改为“squash”,存储后并推出,由于合并后要提交,所以编辑器会提示您编辑这个最新的提交讯息,请编辑讯息后存储并提出,
这样,两个提交就合并成一个提交了,请使用log命令确认历史记录

修改后master分支内容.png



  • 8.查看文件状态
    • $ git status
    • 红色:该文件被添加或者被修改,但是没有添加到git的暂缓区
    • 绿色:该文件在暂缓区,但是没有提交到本地版本库


  • 9.给命令行起别名
    • $ git config alias.st "status"
    • $ git config alias.ci "commit -m"
    • $ git config --global alias.st "status"


  • 10.查看版本信息
    • $ git log:-> 版本号是由sha1算法生成的40位哈希值
    • $ git log -p -2:除显示基本信息之外,还显示每次提交的内容差异,-2意思是仅显示最近两次提交。特别适用于进行代码审查,或者快速浏览某个搭档提交的commit所带来的变化
  • $ git log --graph:查看分支合并图
  • $ git reflog:-> 可以查看所有版本会退的操作(比git log功能更强大)

  • 11.版本回退:
    • $ git reset --hard HEAD:回到当前版本
    • $ git reset --hard HEAD^:回到上一个版本
    • $ git reset --hard HEAD^^:回到上上个版本
    • $ git reset --hard HEAD~100:回到前100个版本
    • $ git reset --hard 版本号(前5位)
  • 12.给log起别名:
    • $ git config --global alias.lg "log --color --graph -- pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev- commit"

  • git add和git commit的原理
    • $ git add:把文件修改或者新添加的文件添加到暂缓区
    • $ git commit:把暂缓区的所有内容提交到当前分枝

  • <h5>Git常见问题</h5>
    • 在使用git对源代码进行push到远程仓库的时候可能会出错,信息如下:


      Snip20170505_1.png
    • 此时很多人会尝试下面的命令把当前分支上传到master分支上:$ git push -u origin master但是任然没有解决问题。
    • 出现错误的主要原因是github/gitlab中的某些文件不在本地代码库目录中,可以通过如下命令进行代码合并【注:pull=fetch+merge】:git pull --rebase origin master,如上方法,即可解决err问题。
  • 注意:git和svn不同,仅仅跟踪文件的变动,不跟踪目录,所以,一个空目录,如果里面没有文件,即便 $ git add 这个目录,另外在别处 checkout 的时候,是没有这个空目录的。
    • 只跟踪文件变化,不跟踪目录,这么设计是有原因的。但这会带来一些小麻烦。有时候,确实需要在代码仓库中保留某个空目录。比如测试时需要用到的空目录。下面来看看如何解决。
      1. 目录是空的:
      • 这种情况下只需要在目录下创建 .gitkeep 文件,然后在项目的 .gitignore 中设置不忽略 .gitkeep就可以了
      • .gitkeep是一个约定俗成的文件名并不会嗲来特殊规则
      1. 目录中一定存在文件
      • 这就需要首先在根目录中设置 !.gitignore,然后在目标目录也创建一个 .gitignore文件,并在文件中设置
*
!.gitignore

git在使用中遇到的问题以及注意点:
1.由于在项目中一般为多人开发,所以在使用git的时候一定要注意:

  • 在更改项目之前,一定要记得先pull项目到最新的版本

  • 如果本地有做项目备份,在commit->pull->push的时候确认是否在备份分支做的操作,如果是,请注意,一定要切回主分支然后merge备份分支的内容,

  • <h5>Git的共享版本库</h5>

    • git服务器的搭建非常繁琐(linux)
    • 可以把代码托管到(GitHub/GitLab/OSChina)
    • 一个文件夹也可以做共享版本库
    • 一个U盘也可以做共享版本库,详见:把Git Repository建到U盘上去
  • 1.一个文件夹作为共享版本库

    • $ git init --bare
  • 2.将共享版本库的所有内容下载到本地

    • $ git clone 共享版本库地址
  • 3.删除忽略文件

    • $ touch .gitignore -> GitHub -> 搜索“.gitignore” -> 选择*最多的 -> 找到Object-C,复制下来
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## Build generated
build/
DerivedData/

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/

## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint

## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output

# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/
  • 4.版本回退
    • $ git reset --hard HEAD^:回到上一个版本(张三)
    • $ git push -f:强制上传至共享版本库
    • $ git reset --hard HEAD^:回到上一个版本(经理)
  • <h5>Git的版本备份</h5>
  • 1.假设Xian1.0版本开发完毕,将1.0版本上传到AppStore,对1.0版本进行备份(打上标签)
    • $ git tag -a Xian1.0 -m "这是Xian1.0版本"
    • $ git tag
  • 2.需要将标签push到共享版本库
    • $ git push origin Xian1.0
  • 3.开始Xian2.0版本的开发
  • 4.发现Xian1.0版本有bug,在经理的文件夹下面创建一个文件夹,用于修复bug,将共享版本库所有内容clon
    • $ git clone
  • 5.将当前的代码转为Xian1.0标签,创建分枝,并切换到该分枝
    • $ git checkout Xian1.0:转为1.0标签
    • $ git checkout -b Xian1.1fixBug:创建分枝,并切换到该分枝
  • 6.在分支中修复bug,上传到AppStore,将修复好的版本,打上tag,并上传至共享版本库
    • $ git tag -a Xian1.1 -m "这是修复了1.0bug的1.1版本"
    • $ git push origin Xian1.1
  • 7.跟当前正在开发的2.0版本进行合并
    • source Control -> pull -> Xian1.1fixBug
  • 8.删除分支
    • $ git branch:查看当前在哪个分支
    • $ git branch -r:查看本地版本库的分支
    • $ git branch -d Xian1.1fixBug:删除本地分支
    • $ git branch -r -d origin/Xian1.1fixBug:删除本地版本库分支
    • $ git push origin --delete Xian1.1fixBug

推荐阅读更多精彩内容