CSS雕虫小技:八卦五行布图法介绍

本质和优点



先申明一下,这里不是在谈玄论道,说八卦五行,我只是利用了一下这几个数字传统的知名度,以及阴阳说中问题归纳和状态演化的技法。

这里所说的布图,就是一种将诺干背景小图片合并在一起(传说中的CSS滑动门技术);这里说的布图说的就是如何组织这些零散的小图片。

口诛笔伐:糟糕的绝对位置布图法:

常见的办法是把一堆相关的图片通过绝对位置放在一个文件中,比如【0,0】放置一个编辑按钮,【0,40】放置一个删除按钮【0,60】放置一个保存按钮。等等。

如此最大的问题是:无关的图片被杂乱无章的堆砌在一起。后期的维护将越来越繁琐。最后将是一场噩梦。

那么,我们如何更好的布图呢?

首先,让我们抛弃我们使用绝对位置的习惯,理由是:

1.绝对位置不便记忆:

绝对位置都是一些数字标示,非常难记,相对位置是个有限集合,绝对位置有无限的状态。

2.绝对位置不便适应需求的变更:

比如我们一个小图片,最初是 18*18,于是,我们布图的时候,间隔为20.

但是后来开发了一套大图标皮肤,小图标的大小变成21*21,溢出了。。。,改吧,慢慢改吧,我们有的是时间。。。

3.绝对位置在编辑的时候也比较麻烦:

而相对位置,你可能只要打开你的布局管理器,轻轻点击几下对齐图标。

如果有一些小调整,绝对位置布局的时候,我们要小心的计算出新的布局位置,更新文档,更新相关代码,通知相关工程师。。。。

4.增加沟通成本:

因为绝对位置的无穷性,我们无法给数值表示的相对位置赋予通用的适合的语意,无穷的状态我们需要依赖无穷的文档。

(我们估计没有这种文档,一旦需要调整,打电话问吧,希望你能找对人。。。)。

而相对位置,只有有限的状态,我们可以清晰的给出位置的约定语意。文档的最高境界也就是不需要文档。

5.糟糕的自适应性:

这点不必解释,绝对布图法根本就不具备这一特征。



太极生两仪,两仪生四相,四相生八卦。

最朴素的图标只有一个状态,这很简单,我们制作一张很朴素的图片即可。

然后我们想加上一个鼠标放上去的激活效果。于是我们有了两种状态。这时候,我们可以采用左右两端布图。

之后呢,鼠标按下去是不是也要有一个新的状态?哦,还差点忘了,更重要的,功能禁用了我们也需要一个状态。至此四大天王到齐了,各自找个角落呆着吧。

我们常见的也就是这四种状态:

1.正常:

2.鼠标放上

3.鼠标点击

4.禁用

如果我把这四种状态自左上起,顺时针排列,我们还可以达到一个惊人的自适应性。

当我们只准备了一个图标的时候,吧他做成整幅度的图片,那么四种状态都显示为正常的图标。

需要增加状态2的时候,只需要扩展右段新图片。同样,一切正常(disablle状态不被支持,显示为正常状态,按下和鼠标移上效果一致也是一个理想的巧合)。

当需要鼠标按下的时候,扩展下方图标。同样,一切正常。

这四种状态的组合,我称之为斜四相。对应样式单为 “.quad-x-”

但是,我们的八卦可是有八个方位的,不要轻易浪费了,怎么办呢?

老子说了:”万物负阴而抱阳,冲气以为和”。

这是我们客观世界普遍纯在的规律。

比如,我们有一个展开的图标,那么我们极有可能需要一个关闭的图标。我们有一个保存按钮,那么,我们极有可能也需要一个打开的功能。

这两类事物相互对立的东西必然有紧密关联,完全应该安排在一起。

于是,我们吧原来的布局位置,顺时针旋转45度,一个正四相产生了。对应样式为:“.quad-y-”

待续。。。。

Extjs.GridPanel显示多行工具栏(tbar)

js文件。。  

Ext.onReady(function() {

	//初始化数据
			var proData = {
				records : [{
							proName : "cocobra 居家内衣系列"
						}, {
							proName : "cocobra 休闲内衣系列"
						}]
			}
			
		
			var proCreate = new Ext.data.Record.create([{
						name : "proName",
						mapping : "proName",
						type : "string"
					}]);

			var fields = [{
						name : 'proName',
						mapping : 'proName'
					}];

			
			var proStore = new Ext.data.JsonStore({
						fields : fields,
						data : proData,
						root : 'records'
					});

			var rowNum = new Ext.grid.RowNumberer();  //添加行号
			var projectColumn = new Ext.grid.ColumnModel([rowNum, {
						header : "项目名称",
						width : 160,
						dataIndex : "proName",
						id : "proName",
						sortable : true  //设置排序
					}]);

			//第二個工具欄
			var tbar2 = new Ext.Toolbar({
						renderTo : Ext.grid.GridPanel.tbar,// 其中grid是上边创建的grid容器
						items : [{
									text : '添加',
									iconCls:'addBtn'
								}, {
									xtype : "tbseparator"
								}, {
									text : "删除",
									iconCls : "deleteBtn"   //图片样式, 需要自己寫css樣式,引入手寫的css,如果用自带会因为浏览器不兼容而不显示图片
								}, {
									xtype : "tbseparator"
								}, {
									text : "删除全部",
									iconCls:'deleteBtn'
								}, {
									xtype : "tbseparator"
								}, {
									text : '保存',
									iconCls:'saveBtn'
								}]

					});

	var tbar3 = new Ext.Toolbar({
				renderTo: Ext.grid.GridPanel.tbar,
				items:[new Ext.form.TextField({
					fieldLabel:"测试"
				//	width:100
					//height:30
				})]
			})



			var projectGrid = new Ext.grid.GridPanel({
						renderTo : "hello",
						title : "项目管理",
						widht : 180,
						height : 200,
						cm : projectColumn,
						store : proStore,
						autoScroll : true, // 内容溢出时产生滚动条
						tbar : [new Ext.form.ComboBox({
									store : ["喜羊羊与灰太狼", "cocobra"],    //給ComboBox添加數據
									emptyText : '请选择供应商',
									id : "provider",
									name : "provider",
									editable : false   //是否允許輸入

								})],
						listeners : {     //將第二個bar渲染到tbar裏面,通过listeners事件
							'render' : function() {
								tbar2.render(this.tbar);
          tbar3.render(this.tbar);
							}
						}
					});
		});

— 在按钮旁边添加图片的css样式

.deleteBtn {
   background-image: url(../images/default/dd/drop-no.gif) !important;
   margin-right:5px;
   background-repeat: no-repeat;
}

.addBtn {
   background-image: url(../images/default/dd/drop-add.gif) !important;
   margin-right:5px;
   background-repeat: no-repeat;
}

.saveBtn {
   background-image: url(../images/default/dd/drop-yes.gif) !important;
   margin-right:5px;
   background-repeat: no-repeat;
}

推荐js组件库dhtmlx

下载地址为http://www.dhtmlx.com/docs/download.shtml

 

这是一个类似ext的东东.

 

下载得到一个zip,解开后目录机构非常清楚,以致于不需要有什么解释,谁都能看懂.

 

如果想看所有,则index.html

 

如果只想看某个js组件功能,进入相应目录即可.其实意思就是,你只需要这个组件,那么你就可以只用部分的js,非常好!

如树组件要1个css,两个js。

 

 

我看了一下它的树,是用表格绘制的,我自己是用的div,没时间细看,功能确实非常强大.什么复选框,拖拽都有,好像要接受xml数据显示.

 

它的html类型是用的html4.01, 而不是我们大家都在用的xhtml 1.0 大家要注意。

 

无论如何,它让开源的世界更加精彩。

 

欢迎交流。

 

我看了一会儿,就决定在今后有空的时候,逐渐把该类库加到我的项目里,最好能和protetype一起用,不知行不行,我还要动画功能,希望可以!

 

 

 

 

 

 

 

 

 

 

 

 

阅读随想(1):《你的灯亮着吗?——发现问题的真正所在》

<meta content=”text/html; charset=utf-8″ http-equiv=”CONTENT-TYPE”>
<meta name=”GENERATOR” content=”OpenOffice.org 1.9.129 (Linux)”>
<meta name=”CREATED” content=”20060312;270700″>
<meta name=”CHANGED” content=”20060315;425500″>
<style type=”text/css”>

<!–
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
H1 { margin-bottom: 0.21cm }
H1.western { font-family: “Arial”, sans-serif; font-size: 16pt }
H1.cjk { font-family: “AR PL SungtiL GB”; font-size: 16pt; font-style: normal; font-weight: bold }
H1.ctl { font-family: “Tahoma”; font-size: 16pt; font-weight: bold }
–>
</style>

作者:fasiondog

我的Blog:http:\\blog.csdn.net\KongDong

第一个问题:问题是什么?

问题解决者的三步:

  1. 问题是什么?

  2. 谁有问题?

  3. 解决者的心态“齿轮从单数变为复数”,把自己从一个问题的解决者转变为一个问题们的解决者(多问题的解决者)。对回答这一问题的每个不同的人群,问:“你的问题本质是什么?”

随想:遇到问题不要急着立即去寻找解决办法,而是要弄清楚“问题是什么?”。如何弄清楚问题的实质,首先需要明确到底是谁的问题。明确是谁的问题并非轻而易举,需要我们善于换位思考,从不同人员的角度出发体验问题的真正所在。否则,只会是拆了东墙补西墙——一塌糊涂!软件开发也好,项目管理也好,都需要我们在众多涉众中进行权衡和明确他们的需要,所有的事情都应该仅仅围绕一个大家共同接受和认可的目标进行,而目标的确定也需要我们事先圈定“大家”的范围,否则,很可能永远没有共同接受的目标。

问题其实就是你期望的东西和你体验的东西之间的差别

幻觉中的问题是真正的问题

对于那些没有幽默感的人,帮他们解决问题简直就是自寻烦恼

随想:一针见血——问题就是期望和体验之间的差别!不禁联想到我们项目经理经常忧心忡忡的模样,苦着脸说“总觉哪里有问题!”。呵呵,这即是好事又是坏事,好事是“觉得有问题的地方通常都一定有问题”——潜在的期望和当前的体验存在差距,知道有问题存在;坏事是“不知道问题在哪?”,这可真是让人郁闷。对于这个问题似乎没有什么解决的灵丹妙药,只有多听听他人意见、对比案例、借鉴历史,多多积累经验了,毕竟要找到自己潜在的“期望”似乎也不那么容易啊!

第二个问题:这个问题是什么?

不要把他们的解决方法误认为是问题的定义

如果你太轻易的解决了他们的问题,他们永远都不会相信你真的解决了他们的问题

道德考虑在遇到有利可图的问题时往往很快就烟消云散了

随想:想起大学时,考试编译原理,自以为复习充分,做起题来酣畅淋漓,连草稿纸都省了,不到半个小时就在众目睽睽之下提前交卷了,满以为必是90分以上,之可惜结果居然只有70多,至今回想,依然愤愤不平!后来,考试数据结构,看着大家一个接一个的提前交卷,我却还在稿纸上比划着最后一道编程题,越来越是心惊,心里直冒汗,心想:“我没那么笨吧,都快走光了!”,结果考试成绩出来,居然是最高分,晕! 啊,似乎和前面的体会有点矛盾,越是心里没底做出来的东西越好?管不了那么多了,就但前面提的是问题解决方法,这儿是考试,不同的:-) 乍一看“道德考虑在遇到有利可图的问题时往往很快就烟消云散”是在告诉我们有利可图时可以暂时放弃道德也无需过于自责,再往后看,才发现作者是要告诉我们,即使是暂时抛弃了道德也不一定会有好结果,所以,做人还是要厚道!保持诚信始终是做人的第一准则,也许会有暂时的误解,但坚持不懈总是会赢来尊重!至于如何做到诚信,不如看看李开复在《做最好的自己》里提到的裁员时是如何对待自己的老同事兼师兄和新员工的。写到这里不由想起两个人来:陈水扁、李登辉…… 不过,值得警惕的是保持诚信绝对不应该是盲目的自以为是,自己觉得自己很讲诚信,而应该是经常自省其身,毕竟无风不起浪,总有自己可能忽视的地方,这却非是什么诚信问题。

不要把问题的解决方法误认为是问题的定义——特别是在你使用自己的解决方法时

你永远都不能肯定你已经有了一个正确的定义,即使在问题已经解决之后

不要过早的下结论,但是也不要忽略你的第一印象

你永远也不能肯定你有了一个正确的定义,但是永远不要放弃需求它的努力

随想:“不要忽略第一印象”每个人应该有不少自己的经历,也再一次从某种程度上验证了“你觉得问题的地方通常一定存在问题”。说实在的,“好像哪儿有问题”是我最喜欢从项目经理们那儿听到的一句话,这也是我自己常常对自己说的一句话。这让我感到高兴,因为这位项目经理开始在琢磨了“到底哪儿存在问题了”,开始在寻找“问题”,虽然这好像捉迷藏,有点难:)

待续……

开发完成后总结心得(团队会议稿)

开发完成后总结心得(团队会议稿)

转载自:  http://www.cnblogs.com/cj723/archive/2006/09/08/498996.html

 

前阶段开发中存在的问题, 及改进建议(下面提到的问题在任何软件公司都会碰到,所以出现也是很正常,在今天讨论后,建议大家在今后的团队运作中尽量避免)

1、前期需求不明,造成设计时目的不明确,开发时时常会因需求问题而困惑,测试人员也会提出一些需求建议,而由于已经开发完成,所以改动起来比较困难。
 改进办法:需求要完全明确是很难做到,但在局部相对独立功能上应该要尽量明确。如:尽量能明确注册需要哪些信息、每个表单是用什么控件、处于什么范围、列表显示哪些字段、查询需要什么条件有明确的说明,这样可以在后期测试时少掉一半的需求建议或bug。

2、原系统有规范但没有较好的执行,由于团队初成立时,无人严格把控各人的代码规范、文件存放、命名等等都存在着很大的问题,而这造成的结果就是后改代码的时间比前面写代码的时间还要长。
 改进办法:项目组长需要每天都check成员代码,保证每天的代码都是相对规范。

3、设计要慎重,应该要足够的考虑,以及和团队的商议,原系统中有一些数据库表的结构和字段值得商榷,如果前期可以大家讨论一下,也许很多问题可以在后来重构中避免。
 改进办法:没有人能一次就设计出完美的东西,需要及时的沟通,包括与客户的反馈,与其他项目组成员的讨论,这样有助于降低开发时偏离需求的风险。也就是说,在开发之前题,是建立在设计者的想法有客户的确认和开发人员的理解的基础之上。

4、开发时因分工不明确,每个页面可能团队所有的人都有修改,这其实出问题的风险是非常大。事实证明,由于数据库存储过程是专人负责,所以不必要的Bug相对较少的,而UI层的不少问题其实都是后者根本不清楚前者的代码意途所致(必要的注释是起码的习惯,松耦合的code是更好的代码风格)。
 改进办法:数据层、逻辑层、UI层,以及UI的各个功能分工,都需要责任到人。

5、代码中重复代码较多,维护时时常会改了一处Bug,却在另一处出现同样的问题,这显然是重复带来的灾难。
 改进办法:开发时,只要是重复代码,就需要考虑是否可以提炼成为函数,并考虑存放到合适的类中(也包括页面html的重复),严禁简单的Ctrl+C到Ctrl+V,这种避免重复代码的做法看似相对麻烦,其实是可以大大减少维护风险。

6、计划不能按期完成,大致三种原因,1、计划不合理;2、人员没有抓紧;3、因其它计划外的原因造成延误
 改进办法:制订计划项目组长需与相关成员讨论以决定计划完成日期,制订时间需要科学合理,如果明确后,相关成员需要尽量按时完成,若有特殊原因,比如技术难题,计划外的事情耽误等等,需要给出理由。再由组长和成员共同商议解决时间,以保证全局的进度不受影响。

7、早期没有存储过程测试,单元测试,页面测试因需求不明,造成测试人员既是测试者又是需求提出和建议者。
 改进办法:需求制订过程需要测试人员全面参与,达到了解足够充分。测试时针对需求做测试用例,以需求为标准,判断开发是否完成或有否错误。

8、前期页面比较混乱,页面布局、样式比较混乱,到处都有如居中、加粗等html语句、列表显示有5种样式等等,造成后期重构非常麻烦。
 改进办法:美工应该在需求制订完成后就介入,进行页面设计,然后.net的aspx页面需要有专人处理,所有的样式必须全部用css统一完成,表单验证、页面跳转需要在开发前完成(甚至最好可以经过测试),这需要界面设计人员(可能是美工也可能是架构师或界面专人)对需求充分了解。

9、项目计划和管理主要以Email和口头传达,过后无法跟踪,造成时间表不明确,人员工作效率不够高,有时很紧张,有时很轻松。
 改进办法:需有项目管理工具,比如VS 2005 Team System或其它项目管理系统。每个人的工作任务需在其中体现,计划安排和调整,相关负责人,延误备注都需要记录。让开发人员保持一个长期的、恒定的开发速度。

总之,由于早期开发时团队人员不整、需求不明、规范实施不利、计划有误等等原因,造成系统开发出现些了问题,但之后有了一定的时间,所以重构已经取得了不少成效,但所谓磨刀不误砍柴工,前期的准备如果充分一些,对后期的维护就会好很多很多。由于时间关系,前期不可能做非常详细的设计,事实上,即使做了详细设计也可能因需求的变更而效用不大,所以更多的是需要大家写出可维护性、可扩展性和可复用性较好的代码,以便更好的适应变化。

最后,以软件大师Robert C.Martin的名言与大家共勉

个体和交互              胜过     过程和工具
可以工作的软件      胜过    面面俱到的文档
客户合作                  胜过    合同谈判
响应变化                  胜过    遵循计划

虽然右项也具有价值,但我们认为左项具有更大的价值。

网站项目管理(一)

作者: Flyingis<o:p></o:p>

不论是功能网站设计,还是基于 B/S 架构的 MIS 系统,都需要有一套合理的管理方案来保证项目的正常运转。去年为政府部门做了一个基于 B/S MIS 系统,项目不大,总共三人——数据库设计人员、程序开发人员(我)和一个测试人员,做完之后有两个体会:一是包揽网站的前后台设计使得自己要面临的问题很繁杂,二是给政府部门做事效率太低,客户的支持不够。不管怎么样,项目总算是做完了,可以交付使用,但中间存在的问题只有我们自己知道,我们想解决问题,但不是我们开发人员说的算。中国这种项目多了,拿出来讲总有讨论不完的话题,并且没有什么意义,这里只想站在项目管理的角度,总结一下网站项目管理中的过程实践,在今后的项目中能做的更好一些。 <o:p> </o:p>

网站界面 <o:p> </o:p>

对于程序员来说,网站界面设计相对于后台程序设计并不那么起眼,因为按照我们的理解,客户需要的是能够使用的程序,这是基础,而不是漂亮的应用界面,如果两者能同时满足当然最好,但在我参与的那个项目中可没有这么好的“待遇”。在实际中,太花哨的界面的确很难得到用户的垂青,特别是专业的 MIS 系统更是如此,因此,在这个过程中如何讲程序的核心功能展现在用户面前是最关键的问题,而不仅仅是按钮工具条如何摆放,核心的模块确定之后,其他的功能和修饰就能很快决定出来。以后应用中,使用 AJAX 是一种增强用户体验的方法,也是当前的流行趋势,但一切还是以实用、简洁、易用为目的。说到这里,这一切还是必须要以完善的需求分析为基础,只有了解到用户需求的核心所在,才能将需求变为程序。 <o:p> </o:p>

项目进度 <o:p> </o:p>

把握好项目进度不是一件容易的事,首先要充分考虑环境因素,如自己的团队怎么样,更重要的是客户的支持与配合。政府部门的项目如果得到领导的充分重视,并且有良好的工作流程和明确的业务关系,项目实施将会非常便利,像银行、电信等部门的项目就相对好做,因为他们的业务需求非常明确,银行的利率计算方法就是明确的,审批流程也是通用的,提取款方式也是规定好了的,后台规则都是不容许轻易改动的等等。但是其他大多数的项目中,开发人员就没有这么好的“待遇”了,一是业务规则经常会出现小的变动,二是有的环节领导还需要一定的“灵活性”,更难受的是,得不到领导的重视,这个项目做的过程难受,做完了以后可能根本不会用,这样虽然可以应付交差,但对开发人员来讲没什么意思,如果可以选择还不如不做。因此,这个环节最重要的是项目需求的精细程度、客户的支持程度和自身的开发实力,这样才能评估出一个较好的项目进度方案,并且项目进行过程中给予控制。 <o:p> </o:p>

人力资源 <o:p> </o:p>

可能在一些大公司情况会好一些,在我参与的这个项目中,自己要负责 Web 的前后台所有应用,从视图层到控制层到业务逻辑层,从更细的层面上说,数据持久化需要自己做,公共类需要自己开发,分页要自己设计,还有各种 VO/BO Action 等等,更头痛的还要设计页面的编排, CSS 控制,起初真的是折腾了我一阵子,现在想想对个人也是一个锻炼,但对于项目而言,应该有充分的人力资源来支撑项目的顺利进行。一般的小项目,需要需求定义人员一名,页面设计美工一名,业务程序开发一到两名,数据库设计人员一名,还需要一位测试人员,这样说的比较狭隘,但至少分工细一点对整个项目的正常运转是有好处的。 <o:p> </o:p>

待续……   《网站项目管理(二)

一个人做项目的感受-CommonsFileUpload文件上传

    到现在才真正体会到一个人做项目的感受,我说的不是一个人做着玩的。在一个公司里给他们做项目管理软件(他们的项目不是软件项目哦)。

    我一个人充当了好多人的角色,我也不认为到现在为止我的水平有多高,但是我现在确实是充当软件架构师的角色。项目到现在做了有两个多月了,在这个过程中出现了不少系统框架上的问题还有数据库设计的不完善造成的一系列问题。从一开始就没有做完整的需求分析,因为那个时候他们也不是很清楚要做一个什么样的东西出来,需求都很模糊。只是一个大概的构想,我但是就想了要把他们这种构想转化为需求的话也就等于是空想。所以只能是走一步说一步。需求是一步一步在变,我好像也是一步一步在改,改东西是最烦人的事情。

    不写了,下班,明天继续。

    接着昨天的写,昏昏沉沉的有敲了一天的代码

    前些天做了有关文件上传的一些东西,现在把它贴出来

用的是Apache的Commons FileUpload组建

    首先是要把需要的jar包放到WEB-INF的lib目录下,需要的jar包全部在附件里,没有的可以去下载

jsp文件:

<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
	<head>
		<title>My JSP 'upload.jsp' starting page</title>
		<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
	</head>
	<body>

		<form action="UploadServlet" method="post" enctype="multipart/form-data">
			<table border="1" align="center">
				<caption>
					请选择要上传的文件
				</caption>
				<tr>
					<td>
						上传人
					</td>
					<td>
						<input type="text" name="name">
					</td>
				</tr>
								<tr>
					<td>
						上传文件
					</td>
					<td>
						<input type="file"  name="file1name">
						<input type="file"  name="file1name">
					</td>
				</tr>
								<tr>
					<td>
						<input type="submit" name="submit" value="上传" >
					</td>
					<td>
						<input type="reset" name="rest" value="重置">
					</td>
				</tr>
			</table>
		</form>
	</body>
</html>

 


 UploadServlet.java

 

package com.xiang;

import java.io.IOException;
import java.io.*;
import java.util.*;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class UploadServlet extends HttpServlet {
	private ServletContext app;

	private String savePath;

	public void init(ServletConfig conf) {
		savePath = conf.getInitParameter("savePath");
		app = conf.getServletContext();// 获取servlet上下文,init方法只执行一次;
	}

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		response.setContentType("text/html;charset=GBK");
		PrintWriter out = response.getWriter();
		request.setCharacterEncoding("GBK");

		DiskFileItemFactory dif = new DiskFileItemFactory();

		ServletFileUpload load = new ServletFileUpload(dif);
		try {
			//工程必须得有commons-io-1.4.jar,不然这个位置会报错
			List items = load.parseRequest(request);// 解析reqeust,包括所传过来的参数,文件等等吧,总之请求过来的所有东西都在这里放着呢

			Iterator it = items.iterator();// 既然items里包含了很多东西,那么我们需要取出每一个看看都是什么东西,主要区分input类型是text还是file
			while (it.hasNext()) {
				FileItem item = (FileItem) it.next();// 取出items里每一个元素,item即可是普通的文本信息,又可以是所上传的文件

				if (item.isFormField()) {// 如果此方法返回真,说明就是普通的文本信息,相反就是文件啦
					// 如果是普通文本信息,简单处理, 输出信息
					System.out.println("表单参数的名字" + item.getFieldName()
							+ "<br/>" + "表单参数的值" + item.getString("GBK"));

				} else {
					// 上传文件
					// 判断用户是否选择的上传文件
					if (item.getName() != null && !item.getName().equals("")) {

						System.out.println("上传文件的大小" + item.getSize());
						System.out.println("上传文件的类型" + item.getContentType());
						System.out.println("上传文件的名称" + item.getName());// 此处获得的名字,是客户端所选择的文件的路径和文件名,而我们往服务器保存时,要从新指定服务器端的路径

						File tempFile = new File(item.getName());// 临时性的,目的调用它的tempFile.getName()方法,仅仅获取所上传文件的名字而不要路径

						System.out.println("文件名字:" + tempFile.getName());
						// app.getRealPath("/");
						System.out.println("当前工程的绝对路径:" + app.getRealPath("/"));
						// 从新获取file,而此file才是真正的所要上传的
						File file = new File(app.getRealPath("/") + savePath,
								tempFile.getName());// 注意新file的构建,逗号前边是路径,后面是文件名

						item.write(file);
						System.out.println("上传成功");
					}

				}

			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}


	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}

 

 

 

 不要忘记在web.xml里田间配置

<servlet>
    <description>This is the description of my J2EE component</description>
    <display-name>This is the display name of my J2EE component</display-name>
    <servlet-name>UploadServlet</servlet-name>
    <servlet-class>com.accp.UploadServlet</servlet-class>
    <init-param>
    <param-name>savePath</param-name>
    <param-value>uploads</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>UploadServlet</servlet-name>
    <url-pattern>/UploadServlet</url-pattern>
  </servlet-mapping>

 

 大功告成

十年总结(18):2006,逐渐走出阴霾

对我过去感兴趣的朋友们,请看十年总结系列文章

---

我的这十年,可能
做出了很多错误决定,如果说这一点,还可以归结为时间的不可逆而无法证实的话,
那么我随波逐流的被动做出很多决定,则是不可否认的事实。

我在一开始就告诫过职场新人,步入职场,要做好规划,要走自己想走的路,
否则有一天,你会突然感到迷茫,到时候再做打算,选择已经不像一开始那么多,
因为在不知不觉间,你已经放弃了太多可能。

不过要做到这一点,的确不容易,做软件这一行,
有多少人致力于埋头钻研技术,以攀上一个又一个技术上的高峰为目标,
似乎忘记了应该从事业的角度设定一系列的里程碑。

年轻多好,因为很多机会,只对年轻人开放。

06年初,公司决定把软件部门出售给另外一家公司,
而曾经二十几人的软件部门,这时候只剩下了四个人。

如果我一直抱有感恩心情的老板都换了,我去还是不去,又是一次选择,
我相信,如果我不去,那么基本上就不会有人去。

然后老板找我谈话,他的意思是希望我们都过去,
因为他说这不是真正的出售,而是一种合作,另外一家公司的老板他是老相识,
他承认自己在管理开发方面存在不足,而另外一个公司在做电信的计费项目,开发氛围和管理方面应该没有问题,
这样两家公司一起推动这个方向,应该会给产品带来一些转机。
既然你都在这个方向上努力坚持了这么多年,如果你相信这个方向没有问题,就应该再坚持下去。

其实这话说到我心里去了,首先我一直认为这个方向没有问题,
更重要的是,做了三年的一个东西,不是说放手就能放手的,
也许换一家公司还可以做同样的事情,同样的方向,甚至获得更好的位置和报酬,
但就像初恋情结,并不是有人更加优秀,你就会轻易移情别恋的。

当然,老板在这几年里投入的几百万,如果我们不过去,那么这些投入就彻底玩完了,
所以,他希望我们过去,肯定也是有他的考虑。

不管怎么说,我还是留下来,带着开发的几个人过去了,
因为我还想证明一点:过去的失败,是不是环境的问题,人员不变的情况下,我要让下一个项目获得成功。

到新公司以后,新老板也不插手这块,终于有了持续的开发时间,
于是重新设计,尤其是界面基本上完全重做,
从3月到9月,半年的连续开发,一个项目顺利上线了,
这一次,终于感觉和用户站在了一个阵营,耳边少了用户不停的抱怨,取而代之的是对系统的肯定和改进建议,
05年被糟蹋的一塌糊涂的自信心,也在这个时候拾回来不少。

1月份过来的时候,有四个人,但其中一位因为家里的原因,4月份走了,实际上,这个项目也就三个人在做,
05年加班加的烦透了,这次并没有刻意安排加班,
只有临近上线的时候紧张了一个月,但也是每天下班晚一些而已。

但这个项目的成功,还远远不够,因为它毕竟只是一个项目,离产品化开发的目标还距离太远,
在这个项目中,本人又充当了一次超级coding高手的角色。
其实如果能够给程序员足够的尊重和物质保障,写代码,尤其是写高质量的代码,是非常快乐的事情。

06年初还不像现在,WEB方面没有那么多成熟的框架,ajax也远没有今天这么普及,
为了改善用户体验,我准备在展示层所有的关键页面都使用AJAX技术,
我也是05年才开始学习WEB相关的东西,对js,css等等都不太熟悉,
于是在很短的时间内,研究了如下技术:
DWR、CSS、VML、DOJO、JSON,XSLT,
最终确定了用DWR作为数据传输手段,json作为数据传输编码格式,用DOJO来完成前台控件开发这样一个模式。
(我有一篇简单的关于DOJO的文章
,提到DOJO在js面向对象封装方面有独到之处)

我用DOJO开发了三套控件,其中最有特色的是一个拓扑图编辑和展示系统,
我对比过,在06年,很多网管都有拓扑图系统,但基本上都是基于C/S的,
我们实现的纯B/S的拓扑系统,在当时还是有一定的先进性的,这一设计,后来变成了我的研究生论文。

这个拓扑系统在浏览器中实现了MVC模式,
model是XML的拓扑描述,通过dwr加载和保存,描述拓扑图上的节点、连接线等信息,
view是VML,用于把模型展示层图形,
controller是javascript,当然使用DOJO做了封装,响应用户的操作,
然后修改模型,从而改变拓扑图的展示,比如拖拽、缩放、画线等等。
这一部分从可研到设计到完成,只用了四周的时间,光js代码就超过了200K,
也算是我比较神奇的发挥了,如果没有DOJO框架,真不知道会乱成什么样。

总之,通过这一个完整项目的顺利验收,
一方面让我的自信心得到恢复,至少相信自己可以推进项目取得成功,而不仅仅是技术达人,
另一方面让我在接触客户方面,有了更多经验,更了解项目的完整过程,
第三,它完善了我的知识体系,原本比较薄弱的WEB前台技术,像JS,CSS,都在这次项目中得到了充分的掌握。

06年10月,我的队伍又开始扩充,以应对接下来的二期,
没有了对自己的怀疑,也没有了公司的决策干扰,我发现一切似乎变得都顺利了,
(新公司的老板一直让我们完全独立,大小事务基本上由我全权决定,当然,招人的数量还是要通过老板拍板)。

我同时发现我的兴趣在逐渐转移,是很自然的,不是刻意为之,
因为在这次项目过程中,我充分认识到,只要有想法,技术上的实现并不是问题,
于是我开始更多的考虑把这个软件做成什么样,
虽然还是热衷于研究一些新的东西,但目标却有所不同,
研究的内容也不都是技术,还包括一些国际标准和规范。

我想,我终于获得了一次比较有价值的成长。

---

今天突然注意到,努力和奴隶的拼音是一样的,真是很有讽刺意味。

某个项目某一页的备份

xml 代码
  1. <%@ page contentType=“text/html;charset=UTF-8” %>  
  2. <%@ include file=“/commons/taglibs.jsp” %>  
  3. <html>  
  4. <head>  
  5.     <%@ include file=“/commons/meta.jsp” %>  
  6.     <link href=“${ctx}/styles/admin/admin.css” type=“text/css” rel=“stylesheet”>  
  7.     <link href=“/superman/styles/G.css” rel=“stylesheet” type=“text/css” />  
  8.     <%@ include file=“/widgets/scriptaculous/scriptaculous.jsp” %>  
  9.     <title>管理员编辑网站信息</title>  
  10. <script type=“text/javascript”>  
  11.    function moveOption(e1, e2)   
  12.    {   
  13.      for(var i=0;i<e1.options.length;i++)   
  14.      {   
  15.         if(e1.options[i].selected)   
  16.         {   
  17.             var e = e1.options[i];   
  18.             e2.options.add(new Option(e.text, e.value));   
  19.             e1.remove(i);   
  20.             ii=i-1   
  21.         }   
  22.      }   
  23.      //传播类型   
  24.      if(e1.name==’trans_modes’||e1.name==’trans_mode_right’)   
  25.      {   
  26.         document.getElementById(‘trans_mode’).value=getvalue(document.getElementById(‘trans_mode_right’));   
  27.      }   
  28.      //传播内容   
  29.      if(e1.name==’trans_contents’||e1.name==’trans_content_right’)   
  30.      {   
  31.         document.getElementById(‘content’).value=getvalue(document.getElementById(‘trans_content_right’));   
  32.      }   
  33.      //违规性质   
  34.      if(e1.name==’inaccuracys’||e1.name==’inaccuracy_right’)   
  35.      {   
  36.         document.getElementById(‘inaccuracy’).value=getvalue(document.getElementById(‘inaccuracy_right’));   
  37.      }   
  38.    }   
  39.   
  40.     function getvalue(geto){   
  41.      var resultArray = new Array();   
  42.      for(var i=0;i<geto.options.length;i++){   
  43.       resultArray.push(geto.options[i].text);   
  44.      }   
  45.      return resultArray.join();   
  46.     }   
  47.        
  48.     //载入页面时添加list内容   
  49.     function fillList()   
  50.     {   
  51.         //传播类型list   
  52.         var modes=document.getElementById(‘trans_mode’).value;   
  53.         //如果没有内容,则直接退出去,以免引起JS的错误   
  54.         if(modes!=””)   
  55.         {   
  56.             var selectComp_transmode_left = document.getElementById(‘trans_modes’);   
  57.             var selectComp_transmode_right = document.getElementById(‘trans_mode_right’);   
  58.             var modearray = modes.split(“,”);   
  59.             //测试代码   
  60.             //alert(“selectComp_transmode_left”+selectComp_transmode_left);   
  61.             //alert(“selectComp_transmode_right”+selectComp_transmode_right);   
  62.                
  63.             //祛除空格   
  64.             for(var i=0;i<modearray.length;i++)   
  65.             {   
  66.                 if(modearray[i]==””)   
  67.                 {   
  68.                     modearray.remove(i);   
  69.                 }   
  70.             }   
  71.             for(var i=0;i<modearray.length;i++)   
  72.             {   
  73.                 var opname=modearray[i];   
  74.                 //取得用户存在的项目ID   
  75.                 for(var j=0;j<selectComp_transmode_left.options.length;j++)   
  76.                 {   
  77.                     if(opname==selectComp_transmode_left.options[j].text)   
  78.                     {   
  79.                        //对用户已选择的ID删除,左边列表 (传播类型)   
  80.                        selectComp_transmode_left.remove(j);   
  81.                        break;   
  82.                     }   
  83.                 }   
  84.                 //对用户已选择的ID插入到右边列表    
  85.                 selectComp_transmode_right.options.add(new Option(opname, i));   
  86.                 //清空缓存   
  87.             }   
  88.         }   
  89.            
  90.         //传播内容list   
  91.         var modes=document.getElementById(‘content’).value;   
  92.         //如果没有内容,则直接退出去,以免引起JS的错误   
  93.         if(modes!=””)   
  94.         {   
  95.             var selectComp_transcontent_left = document.getElementById(‘trans_contents’);   
  96.             var selectComp_transcontent_right = document.getElementById(‘trans_content_right’);   
  97.             var modearray = modes.split(“,”);   
  98.             //测试代码   
  99.             //alert(“selectComp_transcontent_left”+selectComp_transcontent_left);   
  100.             //alert(“selectComp_transcontent_right”+selectComp_transcontent_right);   
  101.             //alert(“adsf”);   
  102.             //祛除空格   
  103.             for(var i=0;i<modearray.length;i++)   
  104.             {   
  105.                 if(modearray[i]==””)   
  106.                 {   
  107.                     modearray.remove(i);   
  108.                 }   
  109.             }   
  110.             for(var i=0;i<modearray.length;i++)   
  111.             {   
  112.                 var opname=modearray[i];   
  113.                 //取得用户存在的项目ID   
  114.                 for(var j=0;j<selectComp_transcontent_left.options.length;j++)   
  115.                 {   
  116.                     if(opname==selectComp_transcontent_left.options[j].text)   
  117.                     {   
  118.                        //对用户已选择的ID删除,左边列表 (传播类型)   
  119.                        selectComp_transcontent_left.remove(j);   
  120.                        break;   
  121.                     }   
  122.                 }   
  123.                 //对用户已选择的ID插入到右边列表    
  124.                 selectComp_transcontent_right.options.add(new Option(opname, i));   
  125.             }   
  126.         }   
  127.            
  128.         //违规性质list   
  129.         var modes=document.getElementById(‘inaccuracy’).value;   
  130.         //如果没有内容,则直接退出去,以免引起JS的错误   
  131.         if(modes!=””)   
  132.         {   
  133.             var selectComp_transmode_left = document.getElementById(‘inaccuracys’);   
  134.             var selectComp_transmode_right = document.getElementById(‘inaccuracy_right’);   
  135.             var modearray = modes.split(“,”);   
  136.             //测试代码   
  137.             //alert(“selectComp_transmode_left”+selectComp_transmode_left);   
  138.             //alert(“selectComp_transmode_right”+selectComp_transmode_right);   
  139.             //祛除空格   
  140.             for(var i=0;i<modearray.length;i++)   
  141.             {   
  142.                 if(modearray[i]==””)   
  143.                 {   
  144.                     modearray.remove(i);   
  145.                 }   
  146.             }   
  147.             for(var i=0;i<modearray.length;i++)   
  148.             {   
  149.                 var opname=modearray[i];   
  150.                 //取得用户存在的项目ID   
  151.                 for(var j=0;j<selectComp_transmode_left.options.length;j++)   
  152.                 {   
  153.                     if(opname==selectComp_transmode_left.options[j].text)   
  154.                     {   
  155.                        //对用户已选择的ID删除,左边列表 (传播类型)   
  156.                        selectComp_transmode_left.remove(j);   
  157.                        break;   
  158.                     }   
  159.                 }   
  160.                 //对用户已选择的ID插入到右边列表    
  161.                 selectComp_transmode_right.options.add(new Option(opname, i));   
  162.             }   
  163.         }   
  164.     }   
  165.        
  166. </script>  
  167. <%–2007/6/2 zhangkai add end –%>  
  168. <body bgcolor=“#F7FDFD” onload=“fillList()”>  
  169. <%– zhangkai 2007/04/25 add start –%>  
  170. <table width=“591” bgcolor=“#F7FDFD”>  
  171.          <tr>  
  172.              <td width=“100%” height=“5”></td>  
  173.          </tr>  
  174. </table>  
  175. <table width=“593” height=“30” border=“0” cellpadding=“0” cellspacing=“0” bgcolor=“#00CCFF”>  
  176.   <tr>  
  177.     <td >  
  178.       <table width=“591” height=“28” border=“0” cellpadding=“0” cellspacing=“0” bgcolor=“#F7FDFD”>  
  179.          <tr>  
  180.              <td width=“100%” style=“FONT-WEIGHT: bold; FONT-SIZE: 14px; COLOR: #000000”>  
  181.                  管理员编辑网站信息   
  182.              </td>  
  183.          </tr>  
  184.       </table>  
  185.       </td>  
  186.   </tr>  
  187. </table>  
  188. <%– zhangkai 2007/04/25 add end –%>  
  189. <%@ include file=“/commons/messages.jsp” %>  
  190. <div id=“htmlcontent”>  
  191. <html:form action=“/netinfo.dox”  method=“POST” focus=“domain” styleClass=“form” >  
  192.     <input type=“hidden” name=“method” value=“save”/>  
  193.     <html:hidden property=“id”/>  
  194.     <%–flag 前一画面传过来的参数,0代表未审批 1代表审批站点–%>  
  195.     <html:hidden property=“flag” value=”<%=request.getParameter(“flag“) %>”/>  
  196.     <table align=“center” width=‘80%’ height=“302”>  
  197.         <tr>  
  198.             <td class=“right” width=“20%”>网站域名:&nbsp;&nbsp;</td>  
  199.             <td width=“30%”>  
  200.             <html:text property=“domain” styleClass=“text” size=“15” readonly=“true”/>  
  201.                         <span class=“req”>*</span>  
  202.             </td>  
  203.             <td  class=“right” width=“20%”>网站名:&nbsp;&nbsp;</td>  
  204.             <td width=“30%”>  
  205.             <html:text property=“sitename”  styleClass=“text” size=“15”/>  
  206.             </td>  
  207.         </tr>  
  208.         <tr>  
  209.             <td class=“right”>IP地址:&nbsp;&nbsp;</td>  
  210.             <td>  
  211.             <html:text property=“ip” styleClass=“text” size=“15”/>  
  212.             </td>  
  213.             <td  class=“right”>法人代表:&nbsp;&nbsp;</td>  
  214.             <td>  
  215.             <html:text property=“corporation” styleClass=“text” size=“15”/>  
  216.             </td>  
  217.         </tr>  
  218.         <tr>  
  219.             <td  class=“right”>联系电话:&nbsp;&nbsp;</td>  
  220.             <td>  
  221.                 <html:text property=“telephone” styleClass=“text” size=“15”/>  
  222.             </td>  
  223.             <td  class=“right”>网站类型:&nbsp;&nbsp;</td>  
  224.             <td>  
  225.                 <html:select property=“types” >  
  226.                     <html:option value=“0”>网络电视台(电台)</html:option>  
  227.                     <html:option value=“1”>视频分享</html:option>  
  228.                     <html:option value=“2”>网络广播电视播放器</html:option>  
  229.                     <html:option value=“3”>BT下载</html:option>  
  230.                     <html:option value=“4”>其他</html:option>  
  231.                 </html:select><span class=“req”>*</span>  
  232.             </td>  
  233.         </tr>  
  234.            
  235.         <tr>  
  236.             <td  class=“right”>联系方式:&nbsp;&nbsp;</td>  
  237.             <td colspan=“2”>  
  238.                 <html:text property=“contact_mode” styleClass=“text” size=“35”/>  
  239.             </td>  
  240.             <td class=“left” >  
  241.                 【联系地址、Email等】   
  242.             </td>  
  243.         </tr>  
  244.         <%–2007/6/2 zhangkai add start –%>  
  245.         <tr>  
  246.             <td class=“right”>音视频节目播放许可证号:&nbsp;&nbsp;</td>  
  247.             <td>  
  248.                 <html:text property=“exequatur” styleClass=“text” size=“15”/>  
  249.             </td>  
  250.             <td class=“right”></td>  
  251.             <td ></td>  
  252.         </tr>  
  253. <%– 2006/5/31 zhangkai delete         
  254.         <tr>  
  255.             <td  class=“right”>省:&nbsp;&nbsp;</td>  
  256.             <td>  
  257.                 <html:text property=“telephone” styleClass=“text” size=“15”/>  
  258.             </td>  
  259.             <td  class=“right”>网站类型:&nbsp;&nbsp;</td>  
  260.             <td>  
  261.                 <html:select property=“types”>  
  262.                     <html:option value=“0”

项目管理之CVS&SVN总结

做了多时的软件开发项目管理,深知代码管理在软件项目开发的过程中的重要位置,于是想到把遇到的关于代码管理方面的经验和问题记下,以供日后补充参考。目
前在这个领域运用最为广泛的莫过于CVS和SVN两者,在实际运用中发现其实他们都同样非常优秀,以下我总结一下 CVS 的日常使用(夹杂与 SVN
的对比):

日常运用 :
cvs init : CVS版本库的初始化
cvs import -m “write some comments here” project_name vendor_tag release_tag : 一个项目的首次导入
cvs checkout(co) project_name : 将代码从CVS库里导出#与SVN同#

cvs update(up) file_name : 将文件同步到最新的版本#与SVN同#

cvs update : 将当前目录同步到最新的版本#与SVN同#

cvs commit(ci) -m “write some comments here” file_name : 确认修改写入到CVS库里#与SVN同#

cvs admin -m 1.3:”write some comments here” file_name : 修改某个版本注释
cvs add new_file : 创建好新文件后添加文件#与SVN同#

cvs add -kb new_file.gif : 按二进制文件方式导入#与SVN同#

cvs admin -kkv new_file.css : 改回ASCII文件方式导入
cvs ci -m “write some comments here” : 然后确认修改并注释#与SVN同#

cvs rm file_name : 将某个源文件物理删除后(删除后需要cvs ci -m “comments”一下)
cvs add dir_name : 添加目录#与SVN同#

cvs log file_name / cvs history file_name : 查看修改历史#与SVN同#

cvs diff(di) -r1.3 -r1.5 file_name : 查看当前文件不同版本的区别#与SVN同#

cvs diff file_name : 查看当前文件(可能已经修改了)和库中相应文件的区别#与SVN同#

* notice :
cvs里没有cvs move或cvs rename,因为这两个操作是可以由先cvs remove old_file_name,然后cvs add new_file_name实现的。

项目发布导出不带CVS目录的源文件#与SVN同#
:
cvs export -r release1 project_name
cvs export -D 20021023 project_name
cvs export -D now project_name

多项目并发管理#与SVN不同#参考文件最后的NOTICE#
:
cvs tag release_1_0 : 建立版本里程碑
cvs commit -r 2 : 开始一个新的里程碑(标记所有文件开始进入2.x的开发)
cvs
rtag -b -r release_1_0 release_1_0_patch proj_dir :
版本分支的建立(在开发项目的2.x版本的时候发现1.x有问题,但2.x又不敢用,则从先前标记的里程碑:release_1_0导出一个分支
release_1_0_patch)
cvs checkout -r release_1_0_patch : 一些人先在另外一个目录下导出release_1_0_patch这个分支:解决1.0中的紧急问题,而其他人员仍旧在项目的主干分支2.x上开发
cvs tag release_1_0_patch_1 : 在release_1_0_patch上修正错误后,标记一个1.0的错误修正版本号
cvs update -j release_1_0_patch_1 : 如果2.0认为这些错误修改在2.0里也需要,也可以在2.0的开发目录下合并release_1_0_patch_1中的修改到当前代码中

cvs (-d :pserver:cvs_user_name@cvs.server.address:/path/to/cvsroot) login : 常见的登陆格式
cvs (-d xxx) passwd -a user_name : 添加用户
cvs (-d xxx) passwd -r system_user user_name : 绑定用户
cvs (-d xxx) passwd -X user_name : 删除用户
* notice :
也可以通过设置CVSROOT系统参数使得所有客户机所有本地用户都可以映射到CVS服务器相应同名帐号
* notice :
将 $Id$ 加在程序文件开头的注释里是一个很好的习惯

CVSNT
里面还有一个问题就是:添加的用户登录时有可能出现 Fatal error, aborting. administrator: no such
user 此类的错误,那么我们需要在CVSNT的Server Settings中把client user设置一下,一般设置成
administrator 即可。

目前 My Team 统一使用 Eclipse 开发工具,以下是一篇关于
Eclipse CVS
使用的文章,以供参考:http://www.eclipse.org/articles/article.php file=Article-
BranchingWithEclipseAndCVS/article1.html.

* notice :
注意右键菜单下面 Team/Compare with/Replace With 菜单的用法基本上就掌握了基本的 Eclipse CVS Plugin 的用法了~
* notice :
svn
的多任务管理与cvs不同,svn中的branch实际上是复制一份当前的repository,然后可以并行地分别修改。复制采用cheap
copy机制,类似于unix系统中的硬链接,branch操作不会使repository所占用的空间倍增,花费的时间也是常数级别的。svn中没有单
独的branch命令,通过svn copy来实现。

这里有一点容易让人糊涂,因为svn中的版本号都是指repository,所以不同branch的版本号是混合交叉的,比如trunk为r60,svn copy之后会创建r61的branch,对trunk修改后再commit就成了r62。

svn
中的merge并非字面上所示的将两个分支归并到一起,而是diff-and-apply的意思,比较两个repository
tree,并将他们的差异归并到working-copy中。这里merge并不区分两个repository
tree是否处于不同的分支,也不会验证working-copy的源头,所以使用这个功能的时候要自己小心。

当我们创建了一个
branch,并且对trunk和branch分别进行了比较大的修改,现在想要把branch中的修改归并到trunk中,此时应该
merge的对象并不是trunk和branch的最新版本,而应该是branch的起始版本和最新版本。因为merge实际上只是做一个diff,所以
前者在将branch的修改归并到trunk的同时也让对trunk的修改丢失了。