作者归档:小艾

使用jenkins job的触发远程构建需要登录或403错误的解决办法

在job的配置中,可以配置“触发远程构建”。配置之后,可以通过访问jenkins对外开放的一条特定url来触发job进行系统构建。对于想在git或svn提交后就进行构建的,是一个十分实用的功能。

触发远程构建配置

但是,如果在jenkins中配置了权限限制,则在一个没有登录jenkins或使用例如curl网络命令去访问这个url时,会提示登录或返回403错误。

要解决这个问题,就需要对匿名用户的权限进行配置了。

经测试,对匿名用户权限进行如下配置后,即可在没有登录的情况下触发远程构建了。

匿名用户权限配置
对于匿名用户,job下的read和build都需要勾选。

2017年05月26日更新

匿名用户权限配置
匿名用户权限中,不需要勾选build也能进行触发远程构建,如果勾选了build,匿名用户查看view时,可以点击构建按钮对项目进行构建,这样就不安全了。

记一次使用高版本jdk接口被坑的经历

最近在一个项目中,使用了一个jdk1.8才有支持的方法:List.sort,开发的时候没有发现注释那写着“since 1.8”,然后打包,发布了。而线上的环境用的是1.7!!!所以在线上访问含有用到这个高版本接口的逻辑的api时,就报500错误了。
我的开发工具是eclipse,本机的jdk版本是1.8,项目是用maven打包的,并且打包时jdk版本设置为1.6。
后来经过研究,发现以下3种情况:

  • 在preference->java->install jres中,将Java se 7选中的话,代码直接报错了。应该是直接改变了整个工作空间的运行环境版本,所以有正确的检验。
    WechatIMG27
  • 如果不改installed jres,而是将server中的运行环境,则会在运行时报error(是error,不是异常)。 通过这样,就可以在本地测试时发现问题了。
    WechatIMG47
    WechatIMG48

  • 改项目的编译版本不起作用,还不知道为什么。

所以后面做项目,看来不仅要将编译等级改成和线上的一致,同时还要将installed jres中改成相同的版本或在server使用的运行环境中设置

C++字符串转码处理

公司项目要做一个桌面应用,需要使用nodejs V8编写C++ addon(扩展)。
在C++的方法中获取入参中含有中文时,使用V8 String类下的Utf8Value类,可以构造出UTF-8编码的字符串,代码如下:

String::Utf8Value param1(args[0]->ToString());

String::Utf8Value重载了operator,返回char 类型,所以可以用

std::string str = string(*param1)

这样的方法来创建一个c++的string对象。
这样对于英文来说,是没有问题,但是对于中文,会出现乱码的情况。
一开始搜索资料时,只是将关键字设定为“char*转string 中文乱码”,能找到一些方法,多数如
http://www.blogbus.com/hx1987-logs/211009970.html
这个blog里面说的,但是都不凑效。(不过也不是没有收获,至少还了解到MultiByteToWideChar这个方法和wchar_t这个类型,C++字符转码都是需要MultiByteToWideChar来转换的)
其实这样搜出来的结果,基本上都有一个共同的问题,就是没有说明这个方法是由什么编码转成什么编码。
因为我入参是UTF8编码,而C++的中文编码,是unicode或GBK,所以查找的方向应该是如果将UTF8编码转换成GBK或unicode编码。
分享两篇博文:
关于C++中文字符的处理
VC URLEncode & UrlDecode (这篇的的话是从另外一个方向找到的:我的一个同事说,应为是用node调用,可以在js中先用encodeURI编码后再调用addon,所以我就往这个方向的搜索了一下,但是最后发现,其实在js中用decodeURI时,也是用UTF8来URIencode,所以在C++中使用URIdecode后,还是UTF8编码,不过里面的Utf8ToStringT方法最终帮我解决了问题^-^)

CString Utf8ToStringT(LPSTR str)
{
    _ASSERT(str);
    USES_CONVERSION;
    WCHAR *buf;
    int length = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
    buf = new WCHAR[length+1];
    ZeroMemory(buf, (length+1) * sizeof(WCHAR));
    MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, length);

    return (CString(W2T(buf)));
}

能得到正确编码的CString,然后就好办了:
CString,string,char*之间的转换(转)

最后在C++中处理完后,还需要返回中文提示信息,所以需要将GBK转成UTF8~~~~下面的一篇问题贴帮到了我:
http://bbs.csdn.net/topics/320078111
GBK转UTF8

void ConvertGBKToUtf8( CString& strGBK )
{
    int len=MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)strGBK, -1, NULL,0);
    WCHAR * wszUtf8 = new WCHAR[len+1];
    memset(wszUtf8, 0, len * 2 + 2);
    MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)strGBK, -1, wszUtf8, len);

    len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
    char *szUtf8=new char[len + 1];
    memset(szUtf8, 0, len + 1);
    WideCharToMultiByte (CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL,NULL);

    strGBK = szUtf8;
    delete[] szUtf8;
    delete[] wszUtf8;
}

使用jQuery的clone+模板的形式动态生成页面

公司的官网改造,其中有些地方要获取服务器返回的json数据,并动态生成列表。

原来的代码是用字符串的形式拼接html标签内容来动态生成的。这样虽然是没什么问题,但是我觉得代码看起来不够整齐,并且后面为何还得在一串String中找到要修改的地方,实在够难受。所以想着能不能在html先写好一个模板,然后在js中复制这个模板,然后修改需要变动的地方后,再嵌入到页面中呢?

有了这个想法,就到网上各种搜,后来发现使用jQuery选择出来的元素对象,有个clone方法,刚好适用。有了可行性后就着手开始尝试了。

1、添加模板

<div id=”news_list”></div>
<div id=”news_templete” style=”display:none;”>
<div class=”divider”></div>
<div class=”media”>
<div class=”blink-dot-top”></div>
<div class=”blink-dot-bottom”></div>
<div class=”blink-dot-right”></div>
<div class=”blink-dot-left”></div>
<div class=”media-left”>
<!– <a href=”#”> –>
<img class=”news_thumd_img media-object” src=”” alt=”…”>
<!– </a> –>
</div>
<div class=”media-body”>
<h4 class=”media-heading”><a class=”news_title_link” href=”#” data-toggle=”modal” data-target=”.bs-example-modal-lg”></a></h4>
<h4 class=”news_time media-heading”>
<small>2015/05/15</small>
</h4>
<div class=”news_body”>
</div>

</div>
</div>
</div>

例如这里的一个id=”news_templete”的div包裹的元素,就可以作为一个模板,用于在页面展示一条新闻。

2、使用js动态复制模板

for(var i = 0 ; i < newsJson.length; i++){

var newTitle = newsJson[i].title;
var newContent = newsJson[i].content;
var newPicUrl=newsJson[i].picUrl;
if(null==newPicUrl||””==newPicUrl){
newPicUrl=”images/123.JPG”;
}
newsListContent.push(newContent);//储存起来,点展开时显示内容
var newContentFormatted = formatContent(newContent);
if(newContent.length>150){
newContentFormatted+=’…’;
}
var newTime=””;
if(newsJson[i].time != null){
newTime = dateTotime(newsJson[i].time);
}else{
newTime = “”;
}
var new_temp_div=$(“#news_templete”);
var newDiv = new_temp_div.clone(true);
newDiv.removeAttr(“id”);
newDiv.find(“.news_thumd_img”).attr(“src”,newPicUrl);
newDiv.find(“.news_title_link”).attr(“onclick”,”javascript:showNewsDetail(“+i+”);”);
newDiv.find(“.news_title_link”).html(newTitle);
newDiv.find(“.news_time”).html(“<small>”+newTime+”<small>”);
newDiv.find(“.news_body”).html(newContentFormatted);
newDiv.show();//取消从模板中带过来的“隐藏”设置
$(“#news_list”).append(newDiv);
}

根据服务器返回的json对象列表,循环生成需要展示新闻列表。

蓝色字体部分,就是使用clone方法复制news_templete模板后,将json的数据填充到这个新div中。这个newDiv就是一条新闻,最后使用append的方式添加到页面中($(“#news_list”).append(newDiv);,其中$(“#news_list”)即为你想要这些内容出现的地方,如第一点中的蓝字html标签)。

这样代码看起来就整齐多了,后面修改也比较容易。如果只是需要修改页面布局,则只需要修改页面中的模板即可。

后来发现公司有用一个react.js这套界面构造框架,用来动态构建界面,感觉还是比较方便。嗯,它是Facebook推出的。

reactjs官网

 

Spring 使用@AspectJ实现aop

《Spring参考手册》中定义了以下几个AOP的重要概念:

  • 切面(Aspect) :官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”,在本例中,“切面”就是类TestAspect所关注的具体行为,例如,AServiceImpl.barA()的调用就是切面TestAspect所关注的行为之一。“切面”在ApplicationContext中<aop:aspect>来配置。
  • 连接点(Joinpoint) :程序执行过程中的某一行为,例如,AServiceImpl.barA()的调用或者BServiceImpl.barB(String _msg, int _type)抛出异常等行为。
  • 通知(Advice) :“切面”对于某个“连接点”所产生的动作,例如,TestAspect中对com.spring.service包下所有类的方法进行日志记录的动作就是一个Advice。其中,一个“切面”可以包含多个“Advice”,例如TestAspect
  • 切入点(Pointcut) :匹配连接点的断言,在AOP中通知和一个切入点表达式关联。例如,TestAspect中的所有通知所关注的连接点,都由切入点表达式execution(* com.spring.service.*.*(..))来决定
  • 目标对象(Target Object) :被一个或者多个切面所通知的对象。例如,AServcieImpl和BServiceImpl,当然在实际运行时,Spring AOP采用代理实现,实际AOP操作的是TargetObject的代理对象。
  • AOP代理(AOP Proxy) 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。默认情况下,TargetObject实现了接口时,则采用JDK动态代理,例如,AServiceImpl;反之,采用CGLIB代理,例如,BServiceImpl。强制使用CGLIB代理需要将 <aop:config>proxy-target-class 属性设为true

通知(Advice)类型

  • 前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。例如,TestAspect中的doBefore方法
  • 后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。例如,TestAspect中的doAfter方法,所以AOPTest中调用BServiceImpl.barB抛出异常时,doAfter方法仍然执行
  • 返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
  • 环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。例如,TestAspect中的doAround方法。
  • 抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。 ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。例如,TestAspect中的doThrowing方法。

好,进入主题。原先在项目中,在controller返回的状态码,是写在返回的json体中的,故无论业务逻辑是否成功执行,在返回到客户端时,Response的status都是正常(即http返回码是200)。现要改成根据controller返回的状态码,设置Response的status。由于已经存在很多controller方法,就不想一个个地找到每个方法的返回的地方来增加处理逻辑,所以决定用AOP来实现。

而我使用的@AspectJ的方式来实现。

首先要引入cglib-nodep.jar这个包,因为要使controller也能凑效,需要使用CGLIB代理。我使用的是3.1版本。

1、先定义切点

import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseStatusPC {

}

2、定义切面,由于这里是controller返回后处理的,所以实现“返回后通知(After return advice)”。

import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.kainaodong.xsst.common.api.constant.CommConstant;
import com.kainaodong.xsst.common.api.domain.ServiceRes;

@Aspect
@Component
public class HttpResponseStatusAspect {
final static Logger log = LoggerFactory.getLogger(HttpResponseStatusAspect.class);
//Controller层切点
@Pointcut(“@annotation(com.kainaodong.xsst.common.api.util.aop.ResponseStatusPC)”)
public void controllerAspect() {
//System.out.println(“controllerAspect”);
}

//Service层切点
@Pointcut(“@annotation(com.kainaodong.xsst.common.api.util.aop.ResponseStatusPC)”)
public void serviceAspect() {
}

@AfterReturning(pointcut=”controllerAspect()”,returning=”res”)
public void doAfter(JoinPoint jp,ServiceRes res){
int reCode=res.getReCode();
if(reCode!=CommConstant.RECODE_SUCCESS){
log.info(“根据controller的返回值设置HttpServletResponse的status:”+reCode);
setResponseStatus(jp.getArgs(), reCode);

}

}

public void setResponseStatus(Object[] args,int status){
for(Object arg:args){
if(arg instanceof HttpServletResponse){
//System.out.println(“HttpServletResponse”);
HttpServletResponse response=(HttpServletResponse) arg;
response.setStatus(status);
break;
}
}
}
}

ServiceRes是封装controller返回值的类。

3、将切点添加到目标对象中:

@ResponseStatusPC
public ServiceRes getUserInfor(HttpServletRequest request,
HttpServletResponse response,Integer userId,Integer loginUserId)

在controller中需要织入通知的方法上,添加@ResponseStatusPC切点的注解即可。

4、修改配置(一般是在applicationContext.xml中配置):

  • 增加切面的bean定义:<bean class=”com.aop.HttpResponseStatusAspect”></bean>  (我开发时,一开始忘了添加,写好切面,添加了切点,却怎样都没有代理成功)
  • 增加自动代理:<aop:aspectj-autoproxy proxy-target-class=”true”/>,如果是代理service,proxy-target-class=”true”可加可不加,而代理controller,则必须要加。

启动项目,即可发现添加了切点的method每次执行完返回后,都会调用HttpResponseStatusAspect.doAfter()。

maven nexus+nginx配置

由于项目中需要用到第三方的推送sdk,而这些sdk都是只提供jar包下载,而我们的项目中是使用maven进行jar包管理。所以只能在自己的服务器上搭建一个maven私服,将这些第三方sdk放到这个maven私服里面进行管理了。

在网上搜了一圈,发现最简单的搭建方法就是使用nexus(不是谷歌亲儿子nexus哦)这个软件进行搭建了。

Nexus官网:http://www.sonatype.org/nexus/

在linux上安装nexus也比较简单,在百度上一搜一大把。以下的步骤引用自:http://blog.163.com/sz2273_pr/blog/static/4126429620135811573231/

nexus安装步骤开始
 1, Nexus 下载

Nexus 官方下载, 最新版下载: nexus-2.4.0-09-bundle.tar.gz

Nexus war下载: nexus-2.4.0-09.war

nexus有两种安装实现方式,一种是war包的方式,将它直接放在例如tomcat下就可以启用的,还有一种源码的方式,做为一个运维还是喜欢源码方式的说哈。开始吧。。。

2, Nexus 安装

解压tar.gz文件:

#cd /home/tools

#tar zxvf nexus-2.4.0-09-bundle.tar.gz

移动目录

#mv nexus-2.4.0-09 /usr/local/nexus

3, Nexus 启动

nexus启动是在bin目录下,首先看一下启动/关闭/重启等命令, 输入命令:

#cd /usr/local/nexus/bin

#./nexus

出现如下选项:

[root@test01 bin]# ./nexus

Usage: ./nexus { console | start | stop | restart | status | dump }

启动nexus:

./nexus  start

关闭nexus:

./nexus  stop

4, Nexus 验证

启动nexus后,在本机浏览器输入地址: http://localhost:8081/nexus

出现上述页面,说明配置nexus成功!

点击右上角“Log in”, 输入用户名和密码(默认用户名:admin      密码: admin123)登录

5.设置

1.点击左侧 Repositories将所有Type 是 proxy 的 configuration配置选项中的 Download Remote Index 配置改为 True

如果需要代理将配置最下面的Override Http Proxy Setting 勾上并填写代理服务器地址和端口,最后保存设置

2.然后在列表中分别右键点击 ReIndex

3、添加自己的jar到nexus

选中 3rd party , Artifact Upload 标签

GAV Definition 选择GAV Parameters

填写 Group Artifact version packaging

然后选择jar包 上传提交即可。

nexus安装步骤结束

另外:在解压出来的nexus目录中,{nexus}/conf/ 下有个名为nexus.properties配置文件,在这里可以配置nexus的端口,仓库的文件的路径等等

其中有一项nexus-webapp-context-path=/nexus的配置,这是指定url中ip:port/XXX中的XXX的,所以如果配置成/nexus的话,你要访问nexus,则要输入ip:port/nexus。

如果要配合nginx使用,则这一项要改成nexus-webapp-context-path=/,即配制成使用ip:port就能访问到nexus。

然后就是配置,nginx了。

我的配置如下:

server {
listen 80;
server_name nexus.kainaodong.com;
location / {
expires -1;
index /;
proxy_pass http://127.0.0.1:9001;
}
include /alidata/server/nginx/conf/rewrite/default.conf;
access_log /alidata/log/nginx/access/default.log;
}

proxy_pass就是配置将对nexus.kainaodong.com子域名的访问转到http://127.0.0.1:9001,也即是我的服务器的nexus上。

一开始,我的nexus的nexus-webapp-context-path这一下没有配置成“/”,而是将nginx的proxy_pass配置成http://127.0.0.1:9001/nexus,这样虽然能访问到nexus,但是里面的仓库内容时访问不到的,也不能进行登录。

然后试着在nexus的server配置中,将base url强制为子域名,这时仓库的内容可以访问了,但是还是登录不了。

 

 

 

QQ截图20150606204706

 

最后在网上又搜了一圈,发现nexus的nexus-webapp-context-path这个属性是可以配置访问路径,所以直接将它改为”/”,nginx中的proxy_pass配置成http://127.0.0.1:9001。

这样,nexus终于能使用子域名来访问了。

 

 

关于mybatis中mapper引用另外一个mapper的resultMap或select的问题

先来看一下两个mapper:

UserMapper.xml——主要完成对用户表(user)的表操作

<mapper namespace=”com.A.xsstuser.impl.dao.UserDao”>
<resultMap id=”BaseResultMap” type=”com.kainaodong.xsstuser.impl.model.User”>
<id column=”user_id” jdbcType=”INTEGER” property=”userId” />
<result column=”nickname” jdbcType=”VARCHAR” property=”nickname” />
<result column=”school_id” jdbcType=”INTEGER” property=”schoolId” />
<result column=”password” jdbcType=”VARCHAR” property=”password” />
……
</resultMap>
……
</mapper>

 UserDetailMapper.xml——主要完成对用户详情表(UserDetail)的表操作

<mapper namespace=”com.A.xsstuser.impl.dao.UserDetailDao”>
<resultMap id=”UserDetailMap” type=”com.kainaodong.xsstuser.impl.model.UserDetail” >
<id column=”user_id” property=”userId” jdbcType=”INTEGER” />
<result column=”realname” property=”realname” jdbcType=”VARCHAR” />
<result column=”gender” property=”gender” jdbcType=”TINYINT” />
<result column=”birthday” property=”birthday” jdbcType=”DATE” />
……
</resultMap>
<sql id=”UserDetail_Base_Column_List” >
user_id, realname, gender, birthday
</sql>
<select id=”selectUserDetailById” resultMap=”UserDetailMap” parameterType=”java.lang.Integer” >
select
<include refid=”UserDetail_Base_Column_List” />
from user_detail
where user_id = #{userId,jdbcType=INTEGER}
</select>
</mapper>

现在有一个需求是需要将User表和UserDetail进行关联查询。我的设计是将这个操作放在UserMapper.xml里面。复杂的关联查询在网上都有很多说明了,这里就不复述了。

既然UserDetailMapper.xml中已经有了映射UserDetail表的列,因此按照编程习惯,肯定要复用一下的,所以根据网上的资料,一开始我在UserMapper.xml里面添加了如下resultMap:

<resultMap id=”UserJoinUserDetail” type=”User”>
<id column=”user_id” jdbcType=”INTEGER” property=”userId” />
<result column=”nickname” jdbcType=”VARCHAR” property=”nickname” />
<result column=”school_id” jdbcType=”INTEGER” property=”schoolId” />
<result column=”password” jdbcType=”VARCHAR” property=”password” />
……
<!– 关联userdetail表 –>
<association property=”userDetail” column=”user_detail” resultMap=”UserDetailMap” />
</resultMap>

保存,然后测试,嗯~妥妥的报错了。看了一下提示,大概就是UserDetailMap不存在的意思。那就奇怪了,明明在UserDetailMapper.xml中定义了的,为什么呢?然后就查看两个mapper,看到<mapper namespace ……>,难道由于namespace不同原因,所以果断将UserDetailMapper.xml的namespace换成和UserMapper的相同的。再测试,这次成功了,真的是namespace的原因。

那这样说,是不是应该同一个模块的mapper的namespace都设成一样呢?我测试了一下,如果改成其他名称如:User,启动的时候会报错(由于mapper是用mybatis的相关功能自动生成的,所以当时只引入了xml,而没将mapper 接口类引入,不然改了UserDetailMapper的namespace时,应该启动就报错)。原因是这个namespace有一个意义是指定相关POJO接口的路径,详情可查看以下链接。

Mybatis的namespace问题说明

那该怎么办,上网找,好像都没有很明确关于如何引用其他mapper文件中的resultMap的文章。那只能再回去看错误日志:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:

Error querying database. Cause: java.lang.IllegalArgumentException: Result Maps collection does not contain value for com.A.kainaodong.xsstuser.impl.dao.UserDao.UserDetailMap

The error may exist in com/A/xsstuser/impl/dao/UserMapper.xml

细心看,其实可以发现,错误中com.A.kainaodong.xsstuser.impl.dao.UserDao.UserDetailMap的UserDetailMap前面还有一串东西,刚好也就是UserMapper的namespace,在UserMapper下当然是找不到UserDetailMap的了,那如果在UserDetailMap前加上UserDetailMapper的namespace的话可能可以,即:

<association property=”userDetail” column=”user_detail” resultMap=”com.A.xsstuser.impl.dao.UserDetailDao.UserDetailMap” />

然后保存,测试,通过。确实可以这样写。

如果不引用resultmap的话,可以应用其他文件的select,如下

<association property=”userDetail” column=”user_id” select=”com.kainaodong.xsstuser.impl.dao.UserDetailDao.selectUserDetailById” /> // 当然也要加上namespace咯。

出现这种问题,也可能由于我对mybatis的运行机制还不太了解。所以写一下这篇文章,整理一下思路。

 

git查看远程版本库和本地库的差异

git 中怎样查看未传送(git push)到远程代码库的(git commit)提交?

1,查看到未传送到远程代码库的提交次数
git status

显示结果类似于这样:

On branch master

Your branch is ahead of ‘origin/master’ by 2 commits.

2,查看到未传送到远程代码库的提交描述/说明
git cherry -v

显示结果类似于这样:
+ b6568326134dc7d55073b289b07c4b3d64eff2e7 add default charset for table items_has_images
+ 4cba858e87752363bd1ee8309c0048beef076c60 move Savant3 class into www/includes/class/

3,查看到未传送到远程代码库的提交详情
git log master ^origin/master

这是一个git log命令的过滤,^origin/master可改成其它分支。

(注:master是当前本地库的分支,origin/master指origin指定的远程库的master分支。当然,master可以改为任意其他分支。)

显示结果类似于这样:
commit 4cba858e87752363bd1ee8309c0048beef076c60
Author: Zam <zam@iaixue.com>
Date: Fri Aug 9 16:14:30 2013 +0800

move Savant3 class into www/includes/class/

commit b6568326134dc7d55073b289b07c4b3d64eff2e7
Author: Zam <zam@iaixue.com>
Date: Fri Aug 9 16:02:09 2013 +0800

add default charset for table items_has_images

总结:
git status 只能查看未传送提交的次数
git cherry -v只能查看未传送提交的描述/说明
git log master ^origin/master则可以查看未传送提交的详细信息

以上内容转载互联网上的。http://bbs.iaixue.com/forum.php?mod=viewthread&tid=1577

由第3点,其实可以想到,将远程和本地分支位置调换一下,即变成git log origin/master ^master,就可以查看远程库比本地库多的内容了。

不过得先执行git fetch origin master命令,将远程库的commit内容同步到本地库。fetch和pull的区别是:fetch只同步远程库的commit信息(log信息),但不会将文件同步到本地,pull则会将文件也同步到本地库。

使用git log的时候,也可以使用 -p 参数查看提交中的更详细信息。