第一步:环境准备
拉取CentOS的镜像,利用Dockerfile文件
代码语言:javascript复制FROM centos:6
MAINTAINER chenyuan
# RUN yum install -y java-1.7.0-openjdk.x86_64 java-1.7.0-openjdk-devel.x86_64
RUN yum -y install openssh-server openssh-clients httpd
# RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config
RUN sed -i "s/#PermitRootLogin yes/PermitRootLogin yes/g" /etc/ssh/sshd_config
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
ENV SSH_PORT 22
# 添加测试用户admin,密码123456,并且将此用户添加到sudoers里
RUN useradd admin
RUN echo "admin:123456" | chpasswd
RUN echo "admin ALL=(ALL) ALL" >> /etc/sudoers
执行如下命令拉取我们需要的镜像,并且是预安装我们想要软件的镜像,所以Dockfile对于我们来说,就是一个配置文件,有了这个东西,我们在哪儿都能获取到相同环境的镜像。
代码语言:javascript复制# 进入Dockfile文件的目录
cd /Users/chenyuan/Tools/Docker
➜ Docker ll
total 8
-rw-r--r--@ 1 chenyuan staff 803B Jun 5 22:38 Dockerfile
# 执行
docker build -t centos6-svn .
当我们安装完毕后,再看看我们的镜像列表
代码语言:javascript复制➜ Docker docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos6-svn latest 394ea6ee9455 15 hours ago 330MB
centos 6 70b5d81549ec 2 months ago 195MB
我们就能看到2个镜像,一个是centos6-svn
,另外一个是centos
的镜像。那我们需要利用这个最基本的镜像启动3个容器来为我们模拟zzhangsan
与lisi
的用户针对于一台SVN服务器
操作搭建环境。
利用Docker来启动3台机器
代码语言:javascript复制#启动暴露端口的容器
docker run -tid -p 5001:22 --name centos-svn-node1 394ea6ee9455 /usr/sbin/sshd -D
docker run -tid -p 5002:22 --name centos-svn-node2 394ea6ee9455 /usr/sbin/sshd -D
docker run -tid -p 5003:22 --name centos-svn-node3 394ea6ee9455 /usr/sbin/sshd -D
解释一下,这行命令代表我需要利用ssh工具并且开启了5001
这样的端口让外接去连接,并且取名字centos-svn-node1
来区别开来。
如果完全启动后,我们看看容器列表具体的情况。
代码语言:javascript复制➜ ~ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
54d68f2ff26b 394ea6ee9455 "/usr/sbin/sshd -D" 15 hours ago Up 15 hours 0.0.0.0:5003->22/tcp centos-svn-node3
9da60e04d8f1 394ea6ee9455 "/usr/sbin/sshd -D" 15 hours ago Up 15 hours 0.0.0.0:5002->22/tcp centos-svn-node2
0d376abb1d82 394ea6ee9455 "/usr/sbin/sshd -D" 15 hours ago Up 15 hours 0.0.0.0:5001->22/tcp centos-svn-node1
我们可以通过iTerm
或者CRT
等其他的Shell
工具来连接,前提是你们通过adduser
获取passwd命令为每台机器添加好相应的用户或者设置好对应的密码。我这里图简单,我就直接用root
用户来操作,生产上建议用不同的用户来管理好权限,这也是用Linux
的一大优势所在。
通过ifconfig
命令找到对应的IP地址连接上去,我截图统一展示一下。
172.17.0.2 centos-svn-node1 # SVN服务器
172.17.0.3 centos-svn-node2 # zhangsan节点
172.17.0.4 centos-svn-node3 # lisi节点
第二步:安装SVN服务端与客户端
我把核心的脚本贴出来,供大家参考:
代码语言:javascript复制# install svn, 在机器centos-svn-node1上面
yum install -y subversion
yum install -y mod_dav_svn
添加仓库
代码语言:javascript复制mkdir -p /data/svn
svnadmin create /data/svn/repo1
chown -R apache:apache /data/svn/repo1
配置svnserve.conf
代码语言:javascript复制vi /data/svn/repo1/conf/svnserve.conf
代码语言:javascript复制# 追加该内容到最后
[general]
anon-access = none
auth-access = write
password-db = passwd
authz-db = authz
创建用户
代码语言:javascript复制htpasswd -c /data/svn/repo1/conf/passwd zhangsan # 创建文件,并且创建zhangsan用户
htpasswd /data/svn/repo1/conf/passwd lisi #创建lisi用户
赋值权限
代码语言:javascript复制vi /data/svn/repo1/conf/authz
代码语言:javascript复制# 追加该内容到最后
[repo1:/]
zhangsan = rw
lisi = r
* =
配置HTTPD
代码语言:javascript复制cd /etc/httpd/conf.d/
代码语言:javascript复制# 追加该内容到最后
LoadModule dav_svn_module modules/mod_dav_svn.so
LoadModule authz_svn_module modules/mod_authz_svn.so
<Location /repo1>
DAV svn
SVNPath /data/svn/repo1
Authtype Basic
AuthName "My Repository"
AuthzSVNAccessFile /data/svn/repo1/conf/authz
AuthUserFile /data/svn/repo1/conf/passwd
Require valid-user
</Location>
启动httpd的服务
代码语言:javascript复制service httpd restart
继续在centos-svn-node2
与centos-svn-node3
上安装SVN的客户端。
yum install -y subversion
在这里基本上就把一个SVN的环境给搭建完毕了。
温馨提示,这里当时是用CentOS7
搭建的,但是会一直报错:Failed to get D-Bus connection: Operation not permitted
,这个错误一直未能找到很好的解决方案,才降级到CentOS6
。如果大家知道好的解决方案,也可以告诉我。
第三步:SVN实操
拉取代码
代码语言:javascript复制mkdir -p /root/repo_client
svn co --username zhangsan http://172.17.0.2:80/repo1 repo_client
提交一点点代码到仓库里去,看看服务端是如何保存我们的文件的。
help
第一个命令,也是最最重要的一个命令
代码语言:javascript复制[root@9da60e04d8f1 demo001]# svn help
usage: svn <subcommand> [options] [args]
Subversion command-line client, version 1.6.11.
Type 'svn help <subcommand>' for help on a specific subcommand.
Type 'svn --version' to see the program version and RA modules
or 'svn --version --quiet' to see just the version number.
Most subcommands take file and/or directory arguments, recursing
on the directories. If no arguments are supplied to such a
command, it recurses on the current directory (inclusive) by default.
Available subcommands:
add
blame (praise, annotate, ann)
cat
changelist (cl)
checkout (co)
cleanup
commit (ci)
copy (cp)
delete (del, remove, rm)
diff (di)
export
help (?, h)
import
info
list (ls)
lock
log
merge
mergeinfo
mkdir
move (mv, rename, ren)
propdel (pdel, pd)
propedit (pedit, pe)
propget (pget, pg)
proplist (plist, pl)
propset (pset, ps)
resolve
resolved
revert
status (stat, st)
switch (sw)
unlock
update (up)
Subversion is a tool for version control.
For additional information, see http://subversion.tigris.org/
add/commit (ci)
添加文件到svn版本中
代码语言:javascript复制[root@9da60e04d8f1 repo_client]# svn add demo001/
A demo001
A demo001/A.txt
[root@9da60e04d8f1 repo_client]# svn st
A demo001
A demo001/A.txt
[root@9da60e04d8f1 repo_client]# svn commit -m 'init'
Adding demo001
Adding demo001/A.txt
Transmitting file data .
Committed revision 1.
在这里,我们的代码就已经提交到服务器上面去了,但是我们知道服务器上文件是如何保存的吗?
代码语言:javascript复制# /data/svn/repo1 仓库地址的目录详情
drwxr-xr-x 2 apache apache 4096 Jun 12 11:00 conf
drwxr-xr-x 3 apache apache 4096 Jun 12 10:47 dav
drwxr-sr-x 6 apache apache 4096 Jun 12 10:55 db
-r--r--r-- 1 apache apache 2 Jun 5 14:48 format
drwxr-xr-x 2 apache apache 4096 Jun 5 14:48 hooks
drwxr-xr-x 2 apache apache 4096 Jun 5 14:48 locks
-rw-r--r-- 1 apache apache 229 Jun 5 14:48 README.txt
SVN在服务器端的存储方式和客户端是不一样的,所以在服务器端是看不到源文件的。服务器端有两种存储方式FSFS和BDB,目前默认都是FSFS。
在这里就不细说了,参考连接:https://www.zhihu.com/question/46768239
checkout (co)
检出代码到本地
代码语言:javascript复制mkdir -p /root/repo_client
svn co --username lisi http://172.17.0.2:80/repo1 repo_client
[root@54d68f2ff26b ~]# svn co --username lisi http://172.17.0.2:80/repo1 repo_client
A repo_client/demo001
A repo_client/demo001/A.txt
Checked out revision 1.
update (up)
代码语言:javascript复制[root@54d68f2ff26b repo_client]# svn up
G demo001/A.txt
Updated to revision 7.
更新到某一个指定版本
代码语言:javascript复制[root@9da60e04d8f1 demo001]# svn up -r r3 A.txt
U A.txt
Updated to revision 3.
[root@9da60e04d8f1 demo001]# svn st
[root@9da60e04d8f1 demo001]# svn cat A.txt
AAAAAAAAAA
BBB
[root@9da60e04d8f1 demo001]# svn up -r r7 A.txt
U A.txt
Updated to revision 7.
[root@9da60e04d8f1 demo001]# svn cat A.txt
AAAAAAAAAA
CCC
CCVCCCCCCCC
DDDD
status
查看本地文件的状态
代码语言:javascript复制[root@9da60e04d8f1 demo001]# svn st
? B.txt # 未加入版本
A C.txt # 第一次加入版本
D D.txt # 已经删除
M A.txt # 编辑文件
查看文件目录层级的状态情况
代码语言:javascript复制[root@54d68f2ff26b repo_client]# svn st -v .
7 7 zhangsan .
7 7 zhangsan demo001
7 3 zhangsan demo001/B.txt
M 7 3 zhangsan demo001/C.txt
M 7 7 zhangsan demo001/A.txt
log
查看提交日志信息
代码语言:javascript复制[root@9da60e04d8f1 demo001]# svn log
------------------------------------------------------------------------
r2 | lisi | 2018-06-12 11:16:40 0000 (Tue, 12 Jun 2018) | 1 line
add D
------------------------------------------------------------------------
r1 | zhangsan | 2018-06-12 10:47:14 0000 (Tue, 12 Jun 2018) | 1 line
init
------------------------------------------------------------------------
查看版本之间某个用户提交的日志集合
代码语言:javascript复制[root@9da60e04d8f1 repo_client]# svn log -r r1:r3 | grep lisi
r2 | lisi | 2018-06-12 11:16:40 0000 (Tue, 12 Jun 2018) | 1 line
查看某个版本具体的信息
代码语言:javascript复制[root@9da60e04d8f1 repo_client]# svn log -r r1 -v
------------------------------------------------------------------------
r1 | zhangsan | 2018-06-12 10:47:14 0000 (Tue, 12 Jun 2018) | 1 line
Changed paths:
A /demo001
A /demo001/A.txt
init
------------------------------------------------------------------------
查看某一个文件
代码语言:javascript复制[root@9da60e04d8f1 demo001]# svn log B.txt
------------------------------------------------------------------------
r3 | zhangsan | 2018-06-12 11:19:53 0000 (Tue, 12 Jun 2018) | 1 line
op
------------------------------------------------------------------------
diff (di)
查看文件不同
代码语言:javascript复制[root@9da60e04d8f1 demo001]# svn diff A.txt
Index: A.txt
===================================================================
--- A.txt (revision 3)
A.txt (working copy)
@@ -1,2 1,3 @@
AAAAAAAAAA
-BBB
CCCCCCCCC
比较一个文件从某个版本到另外一个版本的区别
代码语言:javascript复制[root@9da60e04d8f1 demo001]# svn diff -r r1:r3 A.txt
Index: A.txt
===================================================================
--- A.txt (revision 1)
A.txt (revision 3)
@@ -1 1,2 @@
AAAAAAAAAA
BBB
lock/unlock/info
加锁一个目录/解锁一个目录
代码语言:javascript复制[root@9da60e04d8f1 demo001]# svn lock -m "lock command" A.txt
'A.txt' locked by user 'zhangsan'.
[root@9da60e04d8f1 demo001]# svn info A.txt
Path: A.txt
Name: A.txt
URL: http://172.17.0.2:80/repo1/demo001/A.txt
Repository Root: http://172.17.0.2:80/repo1
Repository UUID: 3bad1814-218a-4a9a-9ccf-b6c7fdf67baa
Revision: 6
Node Kind: file
Schedule: normal
Last Changed Author: lisi
Last Changed Rev: 6
Last Changed Date: 2018-06-13 06:35:18 0000 (Wed, 13 Jun 2018)
Text Last Updated: 2018-06-13 06:35:52 0000 (Wed, 13 Jun 2018)
Checksum: 4ca0a5d1e30df75db6fee65ca9a4e0f1
# 有关锁的相关信息
Lock Token: opaquelocktoken:62e86583-1638-4252-9719-b33f092b50c8
Lock Owner: zhangsan
Lock Created: 2018-06-13 06:37:09 0000 (Wed, 13 Jun 2018)
Lock Comment (1 line):
lock command
[root@9da60e04d8f1 demo001]# svn unlock A.txt
'A.txt' unlocked.
如果另外的用户去操作这个文件,会报错误。
代码语言:javascript复制[root@54d68f2ff26b demo001]# svn commit -m "update the lock file"
Sending demo001/A.txt
Transmitting file data .svn: Commit failed (details follow):
svn: Server sent unexpected return value (423 Locked) in response to PUT request for '/repo1/!svn/wrk/8e7d8d12-c4c6-4ba4-8bc0-fc3ec2c2e522/demo001/A.txt'
当取消掉锁以后,就可以继续提交数据了。但是加锁的时候,数据需要全部已经提交才行。
delete (del, remove, rm)
删除文件的命令
代码语言:javascript复制[root@54d68f2ff26b demo001]# svn st
M C.txt
[root@54d68f2ff26b demo001]# svn rm C.txt
svn: Use --force to override this restriction
svn: 'C.txt' has local modifications
[root@54d68f2ff26b demo001]# svn rm --force C.txt
D C.txt
对于有修改的文件也想删除掉的话,我们就只需要添加一个--force
就好了。
list (ls)
查看服务端的文件列表
代码语言:javascript复制[root@54d68f2ff26b demo001]# svn list http://172.17.0.2:80/repo1
demo001/
[root@54d68f2ff26b demo001]# svn list http://172.17.0.2:80/repo1/demo001
A.txt
B.txt
C.txt
revert
还原本地文件,保持与服务端一致
代码语言:javascript复制[root@54d68f2ff26b demo001]# svn st
M C.txt
M A.txt
[root@54d68f2ff26b demo001]# svn revert -R .
Reverted 'C.txt'
Reverted 'A.txt'
[root@54d68f2ff26b demo001]# svn st
copy (cp)/move (mv, rename, ren)
移动文件或者重命名文件
代码语言:javascript复制[root@54d68f2ff26b repo_client]# svn cp demo001 demo002
A demo002
代码语言:javascript复制[root@54d68f2ff26b repo_client]# svn mv demo002 demo003
A demo003
D demo002/B.txt
D demo002/A.txt
D demo002
其操作就是copy一份,然后把之前的删除掉。
mkdir
创建一个目录
代码语言:javascript复制[root@9da60e04d8f1 repo_client]# svn mkdir testcase
A testcase
cleanup
如果出现被锁住或者其他情况,可以用cleanup
命令来清除
[root@9da60e04d8f1 repo_client]# svn cleanup
switch (sw)
切换工作空间
代码语言:javascript复制[root@54d68f2ff26b test_switch]# svn info
Path: .
URL: http://172.17.0.2:80/repo1/demo001
Repository Root: http://172.17.0.2:80/repo1
Repository UUID: 3bad1814-218a-4a9a-9ccf-b6c7fdf67baa
Revision: 10
Node Kind: directory
Schedule: normal
Last Changed Author: lisi
Last Changed Rev: 8
Last Changed Date: 2018-06-13 07:37:43 0000 (Wed, 13 Jun 2018)
[root@54d68f2ff26b test_switch]# svn sw http://172.17.0.2:80/repo1/demo003
At revision 10.
[root@54d68f2ff26b test_switch]# svn info
Path: .
URL: http://172.17.0.2:80/repo1/demo003
Repository Root: http://172.17.0.2:80/repo1
Repository UUID: 3bad1814-218a-4a9a-9ccf-b6c7fdf67baa
Revision: 10
Node Kind: directory
Schedule: normal
Last Changed Author: lisi
Last Changed Rev: 9
Last Changed Date: 2018-06-13 08:08:29 0000 (Wed, 13 Jun 2018)
cat
通过svn查看文件
代码语言:javascript复制[root@9da60e04d8f1 demo001]# svn cat A.txt
AAAAAAAAAA
CCC
CCVCCCCCCCC
DDDD
changelist (cl)
工作拷贝下面的修改文件分类,可以灵活的使用,做分类提交或者防止部分文件被提交等。
代码语言:javascript复制[root@9da60e04d8f1 demo001]# svn cl one A.txt B.txt
Path 'A.txt' is now a member of changelist 'one'.
Path 'B.txt' is now a member of changelist 'one'.
[root@9da60e04d8f1 demo001]# svn cl two C.txt
Path 'C.txt' is now a member of changelist 'two'.
[root@9da60e04d8f1 demo001]# svn cl three D.txt
Path 'D.txt' is now a member of changelist 'three'.
[root@9da60e04d8f1 demo001]# svn cl
svn: Try 'svn help' for more info
svn: Not enough arguments provided
[root@9da60e04d8f1 demo001]# svn st
--- Changelist 'one':
M B.txt
M A.txt
--- Changelist 'three':
M D.txt
--- Changelist 'two':
M C.txt
提交某一个changelist对应的文件
代码语言:javascript复制[root@9da60e04d8f1 repo_client]# svn ci -m "add changelist" --changelist one
Sending demo001/A.txt
Sending demo001/B.txt
Transmitting file data ..
Committed revision 13.
import/export
import
将本地的文件导入到服务端去
[root@54d68f2ff26b ~]# svn import -m "add new project" projectname http://172.17.0.2:80/repo1/projectname
Adding projectname/release
Adding projectname/trunk
Adding projectname/tag
Adding projectname/branch
Committed revision 11.
[root@54d68f2ff26b ~]# svn list http://172.17.0.2:80/repo1
demo001/
demo003/
projectname/
testcase/
export
导出一个干净的不带.svn文件夹的目录树
[root@54d68f2ff26b ~]# svn export http://172.17.0.2:80/repo1/testcase
A testcase
Exported revision 11.
[root@54d68f2ff26b ~]# cd testcase/
[root@54d68f2ff26b testcase]# svn info
svn: '.' is not a working copy
[root@54d68f2ff26b testcase]#
merge/resolve/resolved
模拟一个生产发版本的例子来处理merge操作,因为用UI界面时间花费的太多太多,用命令行操作速度会快很多倍。
背景:已经在分支branch_20180613_001
上做了一些开发,但是由于发布晚于了别人,其他团队代码已经merge
到了trunk
代码上,现在需要将以最新的代码作为base
分支,然后把自己的改动merge
回来,作为发布分支。
创建分支
代码语言:javascript复制[root@9da60e04d8f1 branch]# svn cp -m "create branch" http://172.17.0.2:80/repo1/projectname/trunk http://172.17.0.2:80/repo1/projectname/branch/branch_20180613_001
Committed revision 15.
[root@9da60e04d8f1 branch]# svn cp -m "create branch" http://172.17.0.2:80/repo1/projectname/trunk http://172.17.0.2:80/repo1/projectname/branch/branch_20180613_002
Committed revision 16.
[root@9da60e04d8f1 branch]# svn list http://172.17.0.2:80/repo1/projectname/branch
branch_20180613_001/
branch_20180613_002/
[root@9da60e04d8f1 branch]#
分支branch_20180613_001
修改后提前上线,合并到了trunk
分支上去了。
现在分支branch_20180613_001
与branch_20180613_002
都对同样的文件有了冲突了,肯定需要在branch
合并后在发布,拉取第三个分支作为发布分支branch_20180613_004
。
第一步:获取分支branch_20180613_002
最早的一个版本
[root@54d68f2ff26b branch_20180613_002]# pwd
/root/workspace/branch_20180613_002
[root@54d68f2ff26b branch_20180613_002]# svn log --stop-on-copy
------------------------------------------------------------------------
r27 | lisi | 2018-06-13 15:15:03 0000 (Wed, 13 Jun 2018) | 1 line
add
------------------------------------------------------------------------
r16 | zhangsan | 2018-06-13 10:58:06 0000 (Wed, 13 Jun 2018) | 1 line
create branch
第二步:试运行merge
动作
# 切换到 branch_20180613_004目录
[root@9da60e04d8f1 branch_20180613_004]# svn merge --dry-run -r16:head http://172.17.0.2:80/repo1/projectname/branch/branch_20180613_002
--- Merging r17 through r27 into '.':
C C.txt
C A.txt
Summary of conflicts:
Text conflicts: 1
Tree conflicts: 1
通过--dry-run
后,大概知道这次合并有多少个冲突,冲突是需要人为去处理的。然后去掉--dry-run
的参数,真正执行合并动作。
[root@9da60e04d8f1 branch_20180613_004]# svn merge -r16:head http://172.17.0.2:80/repo1/projectname/branch/branch_20180613_002
--- Merging r17 through r27 into '.':
C C.txt
C A.txt
--- Recording mergeinfo for merge of r17 through r27 into '.':
U .
Conflict discovered in file 'A.txt'.
Select: (p) postpone, (df) show diff, (e) edit file, (m) merge,
(mc) my side of conflict, (tc) their side of conflict,
# 选择p暂存,后面再处理
(s) show all options: p
Tree conflict on 'C.txt'
> local file obstruction, incoming file add upon merge
Select: (r) mark resolved, (p) postpone, (q) quit resolution, (h) help: p
Summary of conflicts:
Text conflicts: 1
Tree conflicts: 1
[root@9da60e04d8f1 branch_20180613_004]# svn st
M .
C A.txt
? A.txt.merge-left.r16
? A.txt.merge-right.r27
? A.txt.working
C C.txt
> local file obstruction, incoming file add upon merge
Summary of conflicts:
Text conflicts: 1
Tree conflicts: 1
[root@9da60e04d8f1 branch_20180613_004]#
在这个过程中,系统会自动合并一些文件,如果是它处理不了的,就会中断命令窗口,让我们选择。这里对于冲突的文件会4个文件,我详细的描述一下。
代码语言:javascript复制# 冲突文件描述
C A.txt #冲突文件的样子
? A.txt.merge-left.r16 #r16代表该分支服务端最早的样子
? A.txt.merge-right.r27 #r27代表该分支服务端最后的样子
? A.txt.working #代表本地空间最新的样子
通过人工处理后,然后标记其已经解决冲突。看看A.txt
里面的内容
iAAAAAAAAAAAAAA
<<<<<<< .working #代表本地空间最新的样子
RRRRBBBBAAAAAAAAAAAABBBBBBBB
CCCCCCCCC
======= # 分割符号
B22222222222BBBBBBBBBBBi
CCCCCCCCC
>>>>>>> .merge-right.r27#r27代表该分支服务端最后的样子
解决冲突后,我决定的内容是:
代码语言:javascript复制iAAAAAAAAAAAAAA
B22222222222BBBBBBBBBBBi
CCCCCCCCC
然后继续
代码语言:javascript复制[root@9da60e04d8f1 branch_20180613_004]# svn st
M .
M A.txt
C C.txt
> local file obstruction, incoming file add upon merge
Summary of conflicts:
Tree conflicts: 1
[root@9da60e04d8f1 branch_20180613_004]# svn resolve --accept working C.txt
Resolved conflicted state of 'C.txt'
[root@9da60e04d8f1 branch_20180613_004]# svn st
M .
M A.txt
针对于--accept
有如下的参数
--accept ARG : specify automatic conflict resolution source
('base', 'working', 'mine-conflict',
'theirs-conflict', 'mine-full', 'theirs-full')
最后再把代码用commit
提交掉。
上面的这些只是一个svn
的入门,其实还有很多地方都有待去学习,比如更底层的原理等。
参考链接:
1.https://www.kancloud.cn/i281151/svn
2.https://www.howtoforge.com/tutorial/how-to-setup-a-svn-server-on-centos-6/
3.https://www.cnblogs.com/jhj117/p/6256349.html
等等。