对于开发者来说,项目的开发最频繁的就是修修补补,改一下这里改一下那里,结构安排得好的话,改错了可能还能改回来,不好的话......,如果保存了下一次开起来可能就忘记改掉哪里了,这时候就需要一个经常帮你备份的,也就是版本管理工具。
  当然这只要是程序员都应该知道的一件事情,而工具里面,最经常用的也就是以下两个了:Git, Svn,而进一步地,用git的程序员们又相对比Svn多一些。所以今天也既是总结一下两者的用法,也是对两者进行一个比较。

一、 Git

1. Git 的安装

  关于Git安装,官网已经将其整理得明明白白,自行前往这里按照对应系统的版本等,进行下载

2. Git 的配置及 Github

  对于Git来说,最重要的就是和github的搭配了,包括一系列操作,最常见也是最常用的就是建repository,然后将自己的代码挂到上面开源。
  或者是建立一个gitbook或者github.io,挂载上自己写的东西;前者是类似于markdown文件挂到网上查看,后者就是可以成为一个网站(如果你想搞一些小型的类似博客这些展示的,可以采用github.io的方法进行,省去了需要服务器的烦恼,缺点就是访问可能没法那么快)。
常用的配置操作

$ ssh-keygen -t rsa -C "youremail@example.com"  // 创建SSH Key
无需设置密码,所以一路回车就好,
如果顺利会在user下UserName目录中生成一个.ssh目录里面有id_rsa和id_rsa.pub两个文件
id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以告诉他人还有在github上添加的也是这个公钥
登陆GitHub,打开“Account settings”,“SSH Keys”页面,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容
GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了


添加远程库
做全局的配置
$ git config --global user.name "zhangsan"
$ git config --global user.email "zhangsan@139.com"
$ git remote add origin git@github.com:zhangsan/ylez.git // 添加一个远程仓库
  // add origin 就是添加一个远程仓库
  // git@github.com:zhangsan/ylez.git  是远程仓库的地址
  // git@github.com 主机的地址  我们可以通过GitLab大家自己的git服务器
  // zhangsan 是你的用户名
  // /ylez.git 是你的仓库名
$ git push -u origin master  // 将本地的master分支推送到远程的master分支中
$ git push -u origin dev    // 本地切换到dev分支然后将本地的dev分支推送到远程


克隆远程仓库到本地
Git支持多种协议,包括https,但通过ssh支持的原生git协议速度最快。
$ git clone git@192.168.0.8:zhangsan/test.git  // 讲一个远程的仓库克隆到本地

3. Git 的常用命令

(1) 第一部分
$ git init  // 初始化一个Git仓库
$ git status   // 查看仓库的状态
$ git add .   // 将所有修改添加到暂存区
$ git add *  // Ant风格添加修改
$ git add *Controller   // 将以Controller结尾的文件的所有修改添加到暂存区
$ git add Hello*   // 将所有以Hello开头的文件的修改添加到暂存区 例如:HelloWorld.txt,Hello.java,HelloGit.txt ...
$ git add Hello?   // 将以Hello开头后面只有一位的文件的修改提交到暂存区 例如:Hello1.txt,HelloA.java 如果是HelloGit.txt或者Hello.java是不会被添加的
可以多次添加然后在提交
$ git commit -m "comment"  // 将暂存区的修改提交到仓库 后面添加上有意义的注视信息
$ git diff  file  // 在file被修改了还未提交的时候查看修改的部分(和版本库中最新版本的不同 diff == difference 不同)
$ git log  // 查看git的commit信息,每次提交的信息包括注视在内,从最新提交到最久提交
$ git log --pretty=oneline   // 将commit 信息简化成一行显示
注意Git的版本号(commit id)是SHA1值ea34578d5496d7dd233c827ed32a8cd576c5ee85
$ git reset --hard HEAD^   // 退回到相对于当前版本的上一个版本 HEAD 表示当前版本
$ git reset --hard HEAD^^  // 退回到相对于当前版本的上上一个版本  HEAD 代表当前版本
$ git reset --hard HEAD~100 //退回到相对于当前版本的上 100 个版本去  HEAD 表示当前版本
$ git reset --hard 3628164  // 退回到指定的版本   这里不需要全部写commit id  Git 回去自动适配
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针,当你回退版本的时候,Git仅仅是把HEAD从指向append GPL
$ git reflog   // 在退回到旧版本之后可以查看旧版本之前的提交日志
当我们想从一个旧版本退回到新版本但是我们关闭了shell窗口,不能查看之前的commit id了,就可以通过
$ git reflog 查看到之前的版本的commit id
$ git reset --hard 3628164

修改只能在被add 到暂存区以后才能被提交
在file 已经修改还未add的时候
$ git checkout --file  // 表示丢弃工作区的修改退回原始状态(不包括以及添加到暂存区的修改)
file已经修改和添加到了暂存区,还未commit
$ git reset HEAD file   // 丢弃file已经添加到暂存区的修改  HEAD 表示最新版本


如果file修改已经提交到本地仓库
$ git reset --hard HEAD^   //退回到上一个版本


$ rm file  // 从文件系统中删除文件(Git 中还是有记录),
$ git status 的时候Git 会告诉你有一个file 被删除了
$ git rm file  // 从Git版本库中删除文件(同时从文件系统中删除文件) $ git status  正常


$ git rm file  // 从git版本库中删除文件
$ git commit -m "delete file" // 提交删除
$ git checkout file  // 想要从git恢复 会出现:error: pathspec 'README.txt' did not match any file(s) known to git.
$ git reset --hard HEAD^  // 恢复到上一个版本 可以恢复文件


$ rm file   //在文件系统中删除文件
$ git checkout file  // 由于是通过文件系统删除的所以可以通过checkout恢复
(2) 第二部分
每次提交,Git都把它们串成一条时间线,这条时间线就是一个分支
在Git里,默认有一个主分支,即master分支
HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支
master分支是一条线,Git用master指向最新的提交,再用HEAD指向master
每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长
Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上


$ git branch   // 查看git的所有分支
$ git branch dev  // 创建dev分支
$ git checkout dev  // 切换到一个已经存在的分支
$ git checkout -b dev  // 创建dev分支,并切换到dev分支
$ git branch -d dev  // 删除dev分支


$ git checkout master  // 切换到主分支
$ git merge dev   // 将dev分支合并到master分支
Updating d17efd8..fec145a
Fast-forward
 readme.txt |    1 +
 1 file changed, 1 insertion(+)
Fast-forward信息,Git告诉我们,这次合并是“快进模式”,就是直接把master指向dev的当前提交,所以合并速度非常快
$ git branch -d dev  // 删除dev分支
(3) 第三部分
处理冲突
$ git checkout -b feature1
Switched to a new branch 'feature1'


修改readme.txt最后一行,改为:
Creating a new branch is quick AND simple.


$ git add readme.txt
$ git commit -m "AND simple"
[feature1 75a857c] AND simple
 1 file changed, 1 insertion(+), 1 deletion(-)


$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
Git还会自动提示我们当前master分支比远程的master分支要超前1个提交。


在master分支上把readme.txt文件的最后一行改为:
Creating a new branch is quick & simple.


$ git add readme.txt
$ git commit -m "& simple"
[master 400b400] & simple
1 file changed, 1 insertion(+), 1 deletion(-)


$ git merge feature1
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.


$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      readme.txt  // 冲突了
#
no changes added to commit (use "git add" and/or "git commit -a")


Git is a distributed version control system.
Git is free software distributed under the GPL.
Git has a mutable index called stage.
Git tracks changes of files.
Creating a new branch is quick AND simple.


Git用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,我们修改如下后保存:
Creating a new branch is quick and simple.


再提交:
$ git add readme.txt
$ git commit -m "conflict fixed"
[master 59bc1cb] conflict fixed


用带参数的git log也可以看到分支的合并情况:
$ git log --graph --pretty=oneline --abbrev-commit
$ git log --graph --pretty=oneline --abbrev-commit
*   59bc1cb conflict fixed
|\
| * 75a857c AND simple
* | 400b400 & simple
|/
* fec145a branch test
...


删除feature1分支:
$ git branch -d feature1
Deleted branch feature1 (was 75a857c).


通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息
$ git merge --no-ff -m "comment" dev  // 准备合并dev分支,请注意--no-ff参数,表示禁用Fast forward


$ git log --graph --pretty=oneline --abbrev-commit  //图形化展示分支情况
*   7825a50 merge with no-ff
|\
| * 6224937 add merge
|/
*   59bc1cb conflict fixed
...


修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它
在修复bug时候,手里还有工作没有完成但是又不能提交可以将进度冻结
在你当前的工作区(分支)
$ git stash  // 冻结当前的分支修改


查看所有的stash
$ git stash list  // 列出所有的工作现场存储
$ git stash list
stash@{0}: WIP on dev: 6224937 add merge
$ git stash apply (stash@{n}) // 恢复工作现场(只有一个的时候可以省略后面的,如果有多个可以恢复指定的某一个stash)但是不删除存储中的stash
$ git stash drop (stash@{n})  // 删除存储的工作现场(只有一个的时候可以省略后面的,如果又多个就指定删除某一个)
$ git stash pop (stash@{n})  // 恢复的同时把stash内容也删了(只有一个的时候可以省略后面的,如果又多个就指定删除某一个)


$ git branch -d branchName  // 删除某一个分支,前提是在该分支和产生该分支的主分支已经合并了(merge)
$ git branch -D branchName  // 删除某一个分支,无论是否合并都强制删除



当你从远程仓库克隆时,实际上Git自动把本地的master分支和远程的master分支对应起来了,并且,远程仓库的默认名称是origin。


要查看远程库的信息,用git remote:
$ git remote


或者,用git remote -v显示更详细的信息:
$ git remote -v


并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?
  master分支是主分支,因此要时刻与远程同步;
  dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;
  bug分支只用于在本地修复bug,就没必要推到远程了,除非老板要看看你每周到底修复了几个bug;
  feature分支是否推到远程,取决于你是否和你的小伙伴合作在上面开发。


推送的时候如果有人在你之前已经推送了,推送失败
$ git push origin dev
  To git@github.com:michaelliao/learngit.git
   ! [rejected]        dev -> dev (non-fast-forward)
  error: failed to push some refs to 'git@github.com:michaelliao/learngit.git'
  hint: Updates were rejected because the tip of your current branch is behind
  hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
  hint: before pushing again.
  hint: See the 'Note about fast-forwards' in 'git push --help' for details.
因为你的小伙伴的最新提交和你试图推送的提交有冲突,解决办法也很简单,
Git已经提示我们,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送
$ git pull  //抓去远程的分支的提交到本地


没有指定本地dev分支与远程origin/dev分支的链接,推送也会失败,所以首先我们需要建立本地分支和远程分支的联系
$ git branch --set-upstream dev origin/dev  // 建立本地dev 分支和远程dev分支的联系

参考git命令大全(非常齐全)


二、 Svn

1. Svn 的安装

(1) Windows 环境

  Window环境下,直接点击这里下载Svn客户端,然后按正常操作安装即可

(2) Linux 环境
// ubuntu环境下的话直接运行
sudo apt-get install subversion

2. Svn 的配置

(1) 客户端的配置

  这个配置其实没有涉及到很多东西,Windows环境直接右键就看到SVN以插件的形式集成到其中,可直接点开界面进行一些基本的配置。
  Linux环境则需要记住常见的那几句命令,拉取项目,更新代码,提交代码,添加、删除文件等。

(2) 服务端的配置

  服务端的配置的话一般相对麻烦一点,第一步自然是安装,这里不再赘述

① 安装 SVN
② 在任意目录下(一般是系统目录)(linux 为 home)下建立文件夹
mkdir /home/(yourportfolioName) etc: svn
③ 创建数据仓库(可以根据需要创建多个)
svnadmin create /home/svn/test   (仓库里面可以放很多项目)
④ 启动 svn 网络服务
svnserve -d -r /home/svn    (不要带上版本仓库)

其中 -d 参数让 svnserve 运行在后台,-r 参数限定了数据仓库,在网络上可以访问的地址。
/home/svn指定svn数据仓库存放的目录位置
⑤ 设置 svn: 方式的访问权限
在每个数据仓库目录下都有一堆目录,进入 conf 并打开 svnserve.conf 这个文件,找到以下几行,
 并把前面的注解符号 ‘#’ 去掉,注意千万在每行的前面别留任何空格。

anon-access = read auth-access = write password-db = passwd

其中 anon-access 和 auth-access 分别为匿名和有权限用户的权限,默认给匿名用户只读的权限。

但如果想拒绝匿名用户的访问,只需把 anon-access的read 改成 none 就能达到目的。
password-db 后的 passwd 则为定义用户名及密码的文件路径。这个文件默认跟 snvserve.conf 在
同一个目录,下面看看怎么配置 passwd文件中的内容。

[user] # harry = harryssecret # sally = sallyssecret
harry 和 sally 这两行分别定义了可以对 repo 进行写入的用户的用户名和密码。我们可以在下面加上
自己的用户名和密码,比如

zhangsan = zhangsan

这样就给了用户 testsvn 往 repo 中写的权限。存盘退出后,svn 服务器就算正式可以运行了!

把所有需要管理的项目都放在 /home/svn这个目录下,像正常那样用 svnadmin create 命令建立数据仓库。

这样就可以使用svn客户端通过 “svn co svn://IP地址/数据仓库目录” 的方式把服务器上的数据仓库拉到本地了。

E000061: Unable to connect to a repository at URL 是svn的端口没有打开,svn的默认端口是3690,

在ubuntu里查看端口打开情况,命令是netstat -anp |grep 3690,

查看进程的命令:ps -ef |grep "svn"

把svn服务重新启动下就可以了,命令为svnserve -d -r /home/svn
⑥ 用 improt 导入工程
命令为:svn import  /home/username/myproject   svn://192.168.1.1:3690/test/edusoho  -m  "我的工程"

3. Svn 的常用命令

(1) 将文件 checkout 到本地目录
svn checkout path(path是服务器上的目录)
例如:svn checkout http://ip地址/路径/项目名
简写:svn co
(2) 往版本库中添加新的文件
svn add file
例如:svn add test.php(添加test.php)
svn add *.php(添加当前目录下所有的php文件)
//需要add的都是新文件,如果只是在旧文件上改动不需要add,直接commit即可
(3) 将改动的文件提交到版本库
svn commit -m “LogMessage“ [-N] [--no-unlock] PATH        (如果选择了保持锁,就使用–no-unlock开关)
例如:
  svn commit -m “add test file for my test“ test.php
简写:
  svn ci
//推荐在项目根目录下commit,无需指定是哪个文件,一键上传改动的那些
(4) 加锁/解锁
svn lock -m “LockMessage“ [--force] PATH
例如:svn lock -m “lock test file“ test.php
svn unlock PATH
// 不常用
(5) 更新到某个版本
svn update -r m path
例如:
svn update如果后面没有目录,默认将当前目录以及子目录下的所有文件都更新到最新版本。
svn update -r 200 test.php(将版本库中的文件test.php还原到版本200)
svn update test.php(更新,于版本库同步。如果在提交的时候提示过期的话,是因为冲突,需要先update,修改文件,然后清除svn resolved,最后再提交commit)
简写:svn up
//在产生了错误,覆盖了原来版本后,可以使用这个恢复到较近正确的版本
(6) 查看文件或者目录状态
svn status path(目录下的文件和子目录的状态,正常状态不显示)
【?:不在svn的控制中;M:内容被修改;C:发生冲突;A:预定加入到版本库;K:被锁定】
svn status -v path(显示文件和子目录状态)
第一列保持相同,第二列显示工作版本号,第三和第四列显示最后一次修改的版本号和修改人。
注:svn status、svn diff和 svn revert这三条命令在没有网络的情况下也可以执行的,原因是svn在本地的.svn中保留了本地版本的原始拷贝。
简写:svn st
(7) 删除文件
svn delete path -m “delete test fle“
例如:svn delete svn://192.168.1.1/pro/domain/test.php -m “delete test file”
或者直接svn delete test.php 然后再svn ci -m ‘delete test file‘,推荐使用这种
简写:svn (del, remove, rm)
//推荐删除文件时用svn删,手动删除的话只是本地删除,远程没删除的话容易导致下次拉取时出错
(8) 查看日志
svn log path
例如:svn log test.php 显示这个文件的所有修改记录,及其版本号的变化
(9) 查看文件详细信息
svn info path
例如:svn info test.php
(10) 比较差异
svn diff path(将修改的文件与基础版本比较)
例如:svn diff test.php
svn diff -r m:n path(对版本m和版本n比较差异)
例如:svn diff -r 200:201 test.php
简写:svn di
(11) 将两个版本之间的差异合并到当前文件
svn merge -r m:n path
例如:svn merge -r 200:205 test.php(将版本200与205之间的差异合并到当前文件,但是一般都会产生冲突,需要处理一下)
(12) SVN 帮助
svn help
svn help ci
(13) 版本库下的文件和目录列表
svn list path
显示path目录下的所有属于版本库的文件和目录
简写:svn ls
(14) 创建纳入版本控制下的新目录
svn mkdir: 创建纳入版本控制下的新目录。
用法: mkdir PATH…
mkdir URL…
创建版本控制的目录。
每一个以工作副本 PATH 指定的目录,都会创建在本地端,并且加入新增
调度,以待下一次的提交。
每个以URL指定的目录,都会透过立即提交于仓库中创建。
在这两个情况下,所有的中间目录都必须事先存在
(15) 恢复本地修改
svn revert: 恢复原始未改变的工作副本文件 (恢复大部份的本地修改)。revert:
用法: revert PATH…
注意: 本子命令不会存取网络,并且会解除冲突的状况。但是它不会恢复
被删除的目录
(16) 代码库 URL 变更
svn switch (sw): 更新工作副本至不同的URL。
用法: 1、switch URL [PATH]
2、switch –relocate FROM TO [PATH...]
 1、更新你的工作副本,映射到一个新的URL,其行为跟“svn update”很像,也会将
   服务器上文件与本地文件合并。这是将工作副本对应到同一仓库中某个分支或者标记的
   方法。
 2、改写工作副本的URL元数据,以反映单纯的URL上的改变。当仓库的根URL变动
  (比如方案名或是主机名称变动),但是工作副本仍旧对映到同一仓库的同一目录时使用
  这个命令更新工作副本与仓库的对应关系。
(17) 解决冲突
svn resolved: 移除工作副本的目录或文件的“冲突”状态。
用法: resolved PATH…
注意: 本子命令不会依语法来解决冲突或是移除冲突标记;它只是移除冲突的
相关文件,然后让 PATH 可以再次提交
(18) 忽略掉某些文件
svn propset svn:ignore node_modules .

用法:在根目录下失手使用svn add * 后的补救措施,忽略掉node_modules文件夹
注意:语句最后的点不能省略,文件夹目录不能加斜杠

参考svn常用命令集合,至于Windows用户还是用界面操作,不用记那么多命令吧。


三、 Git 与 Svn 的比较

  本人水平有限,使用这两个工具时间也还不够长,尤其是在实际接触开发用的还是SVN比较多,无法很好的评判两个工具之间的优缺点。这里找到了一个较为详细的分析,也是比较完整的。
  详情请参考SVN与Git比较的优缺点差异


四、 结语

  写这篇文章的初衷,即是能帮助像我一样对这两个工具不熟悉的人尽快上手,能更好的适应项目开发,将项目的版本更好的进行管理,减少因版本管理的问题导致的做无用功,重复的工作。
  内容也比较多和长,如果本篇文章对你有帮助,请转发给更多的人儿,谢谢。


  • alipay
  • wechat