关于JVM的那些事儿(A)

什么是 JVM:

            JVM是Java Virtual Machine的缩写。它是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。

JDK、JRE、JVM关系:

      JRE:JavaRuntimeEnvironment,所有编译过后的Java 程序都要在JRE下才能运行。普通用户只需要按照JRE,运行class文件即可。
      JDK:Java Development Kit是developers用来来编译、调试java程序用的开发工具包。JDK的工具也是Java程序,也需要JRE才能运行。为了保持JDK的独立性和完整性,在JDK的安装过程中,JRE也是 安装的一部分。
     JVM:JavaVirtualMachine,为JRE的一部分,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。使用JVM就是为了支持与操作系统无关,实现跨平台。

 

JVM class 加载机制:

         类加载机制,大致可分为:加载、验证、准备、解析、初始化、使用、卸载等七个过程,而类的加载由Bootstrap ClassLoader 及其子类完成,以下将详细介绍其具体过程。

         编译:此过程将把.java的文件编译成.class 文件,前者为源文件,后者字节码文件,此过程主要检验编译错误,例如赋值错误、缺少字符、包引用错误、对象、函数引用错误等。

         类加载:类的加载主要由根加载器及其子类完成,分为Bootstrap ClassLoader、Extension ClassLoader、Application ClassLoader、以及自定义的加载器。在类的加载阶段,将在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的入口。需要注意的是,类的加载并非只能从class文件或者,也能从jar包或war包等包中获取,当然也能通过动态代理以及其它文件(如jsp)生成。当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。采用双亲委派的一个好处是比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个Object对象。

        验证:这一阶段的主要目的是为了确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

       准备:该阶段是正式为类变量分配内存以及为基础类型设置初始值,即在方法区中分配这些变量所使用的内存空间。例如public static int a=1000,此时a将被初始化为0而非1000。

      解析:该阶段,将常量池中的符号引用,改为直接引用。其中,符号引用对于目标地址是否存于内存中无硬性要求,直接引用,则需要保证其存在。

      初始化:该阶段是类加载最后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载器以外,其它操作都由JVM主导。到了初始阶段,才开始真正执行类中定义的Java程序代码。类的初始化,是自上由下的进行,即先进行父类初始化,再进行子类初始化。在类内部,其初始化步骤为:静态变量、静态初始化块、变量、初始化块、构造器,当存在继承时,其初始化步骤为:父类–静态变量、父类–静态初始化块、子类–静态变量、子类–静态初始化块、父类–变量、父类–初始化块、父类–构造器、子类–变量、子类–初始化块、子类–构造器。

图1.类加载流程

JVM执行引擎:

        1.代码解释器。 
        2.JIT代码生成器。该方式第一次执行的字节码会编译成本地的机器代码,被缓存在系统中,以后可以随时调用。
        3.自适应的优化器。这种方法里,虚拟机开始的时候解释字节码,但是会监控运行中程序的活动,并记录下使用最频繁的代码段,虚拟机会把这些活动最频繁的代码段编译成本地代码。
其实以上提的都是软件实现的虚拟机,还有一种虚拟机是由硬件芯片构成,它用本地方法执行java字节码。

JVM内存划分:

           JVM内存主要划分为六大块,即方法区、java堆、java栈、程序计数器、本地方法栈。

          方法区:存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息(其中静态变量放于常量池中)。当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时,这些数据都来源于方法区域,同时方法区域也是全局共享的,在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。

         JVM堆:JVM中的堆内存,为所有线程共享,用于存放对象实例,几乎所有的对象都在此处分配内存。堆内存 占据了JVM大多数内存空间,其存于一片不连续存储块上,而我们熟知的GC即垃圾回收,发生在该空间,由于垃圾回收机制采用分代回收算法,因此可将堆细分为新生代、老年代。新生代又有Eden空间、From Survivor空间、To Survivor空间三部分。而永久代(非堆内存,JDK1.8之前)的数据不会被更新,例如intern()产生的对象,并不会被回收,因此程序操作不当,可能导致永久代数据量过大,可能会导致OOM问题。在jdk1.7以后,引入了元空间,废除了永久代。关于各引用的GC,读者可自行阅读之前的文章。

      JVM栈:JVM栈分为本地方法栈与java栈,栈为线程私有,其生命周期与线程相同,每个方法被执行时,都会创建一个栈帧,用于保护现场,其中存储局部变量表、操作栈、动态链接、方法出口等信息。当线程请求的栈深度超过最大值,会抛出 StackOverflowError 异常。当java需要调用c、c++等非java程序时,会创建本地方法栈,对于大多数程序来说,基本上不会用到,当然其原理与前者大致类似,在此不做赘述。

      程序计数器:线程私有,它的生命周期与线程相同。可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,如:分支、循环、跳转、异常处理、线程恢复(多线程切换)等基础功能。如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Natvie方法,这个计数器值则为空(undefined)。
程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,所以此区域不会出现OutOfMemoryError的情况。

git历史分支修复

背景介绍:某人A与某人B在开发特性分支完成后,往验证分支合并时,出现了冲突,但是A或者B在合并时,并没发觉,当分支上了主干分支后,特性分支被删除,但某一天在合并其它分支或代码检查时,发现主干分支代码存在问题,此时需要做分支fix,即将A与B编写的分支代码合并一次,然后再往主分支以及其它验证分支合并。然而令人意想不到得是:此时特性分支已经删除!面对此情此景,那么我们该如何修复主干或者验证分支代码呢?是拉取主干分支,重新编写一次代码呢?还是在直接在主干分支修改呢?

当然,这个案例可能比较特殊,我们现在不妨假设特性分支并未删除,我相信很多人都能想到解决方案,即将两个特性分支合并,再往验证分支合并,待验证通过后,往主干分支合并即可。那么现在的问题,即是怎么找回分支!我相信大家可能有各种各样不同的解决方式,现在就个人意见,分享下本人的处理方案。其处理流程如下图1所示。

修复流程图
图1   修复流程图

正如上图所示,其核心命令即为:git checkout commit_id -b newBranchName,其中commit_id 为commit 提交记录id,可通过git log 找寻,若集成到了第三编辑器,可直接查看文件提交commit.根据commit id 复制的分支,与原有分支一模一样,因此可安全使用。而其它操作例如合并分支,提交分支,由于过于简单,在此不做介绍,若不懂之处,可自行百度,在此不做过多介绍。

 

linux下简易部署方案

当我们提起自动化部署方案时,不同的人、不同的公司总会有不同的想法以及方案,有人钟爱Jenkins+Maven+Shell,也有人偏爱git-ci,甚至有人更喜欢手动部署。本文将基于bat+shell+plink+sftp实现远程linux服务器部署,其中bat脚本用于win下文件校验、复制以及打包;shell用于linux下文件移动、删除以及重启;plink用于连接远程linux服务器;sftp用于压缩文件传输。其具体过程如下图所示。

2
部署流程示例图

如下为bat脚本示例,读者可根据需要进行修改

@echo off
:begin
::%cd%为当前文件目录
set deploy_dir=%cd%\..\
:directory
::项目文件目录
set project_dir=C:\临时文件\CMCC_NEW
:selectItem
set /p configSelect=please select scdev,scsit or uat:
::user name
set /p user=please enter username
::password
set /p pass=please enter password
set flag=1
::配置对应ip,
if %configSelect% EQU scdev ( set flag=2
set ip=10.0.0.1
set upload_file=upload_39.txt )
if %configSelect% EQU scsit ( set ip=10.1.1.1
set flag=2
set upload_file=upload_40.txt )

if %flag% EQU 1 ( echo “select error,please select scdev or scsit again”
goto selectItem )
if exist %project_dir%\fssc-base\fssc-base-core\src\main\resources\profiles\scsit\*.* (
goto checkDeploy )
echo config File error ,please check file
goto selectItem
:checkDeploy
if exist %cd%\plink.exe ( if exist %cd%\psftp.exe (goto manageFile))
set /p project_dir= please enter deploy directory:
goto checkDeploy
:manageFile
del /f /s /q %deploy_dir%\uploadFile\*
::class文件
xcopy /y/s %project_dir%\fssc-aggregator\target\fssc-aggregator\*.* %deploy_dir%\uploadFile
::配置文件
xcopy /y/s %project_dir%\fssc-base\fssc-base-core\src\main\resources\profiles\%configSelect%\*.* %deploy_dir%\uploadFile\WEB-INF\classes\
::cas
xcopy /y/s %project_dir%\fssc-base\fssc-base-core\src\main\resources\profiles\cas\spring\*.* %deploy_dir%\uploadFile\WEB-INF\classes\spring\
::JMS
xcopy /y/s %project_dir%\fssc-taxp\fssc-taxp-core\src\main\resources\profiles\scsit\spring\applicationContext-beans-jms.xml %deploy_dir%\uploadFile\WEB-INF\classes\spring\
pushd %deploy_dir%\uploadFile
jar -cvf CMCC.war ./*
pushd %deploy_dir%\psftp
::传输文件
psftp -l %user% -pw %pass% -b %upload_file%
pause
::部署脚本
plink.exe %user%@%ip% -pw %pass% “sh /home/infraadmin/java/apache-tomcat-tax/app/linux_deploy.sh;”
pause

:end

如下为40.txt文件内容

open 10.0.0.1 22
cd /home/infraadmin/java/apache-tomcat-tax/app
put -r ..\uploadFile\CMCC.war
bye

如下为bash脚本

#!/bin/bash
cd /home/infraadmin/java/apache-tomcat-tax/app
##cd /var/CommonSoftware/Tomcat/apache-tomcat-9.0.8/app
if [ -f “old.war” ]; then
rm -rf old.war
fi
if [ -f “tax.war” ]; then
mv tax.war old.war
fi

if [ -f “../webapps/tax.war” ]; then
rm -rf tax.war
fi
mv CMCC.war tax.war
cp ./tax.war ../webapps/tax.war
cd ../bin
##判断Tomcat-tax是否已经启动
count=`ps -ef |grep apache-tomcat |grep -v ‘grep’|wc -l`
if [ 0 == $count ];then
rm -rf ../webapps/tax
sh ./startup.sh
else
sh ./shutdown.sh
rm -rf ../webapps/tax
sh ./startup.sh
fi

以上为本人编写的一个简单脚本示例,其中复制的class文件为开发项目时的运行文件,读者可根据需求,使用maven等工具进行打包。当然最重要的还是plink插件以及sftp插件,此插件可能存在版权问题,请勿用于商业用途。

若读者有兴趣,可点击连接下载

关于ThreadLocal

一、概述

ThreadLocal虽与thread相似,然而它并不是一个Thread,而是线程局部变量,顾名思义,它就是为每一个使用该变量的线程都提供一个变量值的副本,其底层为弱引用map,它是Java中一种较为特殊的线程绑定机制,可以简单的理解为以当前线程为key的map(与真正实现稍有差异)。在多线程情况下,每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。与锁机制相比,使用ThreadLocal消耗了内存,节约了cpu,属于以空间换时间的应用。

从thread的角度看,每个thread都保持一个对其threadlocal副本的引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在thread完成之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用,而关于何时GC,可自行百度java四类引用)。而通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多thread环境常出现的并发访问问题提供了一种隔离机制。那么ThreadLocal是如何做到为每一个thred维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个ThreadLocalMap,用于存储每一个线程的变量的副本。

二、API说明

ThreadLocal()
创建一个线程本地变量。

T get()
返回此线程局部变量的当前线程副本中的值,如果这是线程第一次调用该方法,则创建并初始化此副本。

protected T initialValue()
返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。而在程序中一般都重写initialValue方法,以给定一个特定的初始值。若该实现只返回 null;如果程序员希望将线程局部变量初始化为 null 以外的某个值,则必须为 ThreadLocal 创建子类,并重写此方法。通常,将使用匿名内部类。initialValue 的典型实现将调用一个适当的构造方法,并返回新构造的对象。

void remove()
移除此线程局部变量的值。这可能有助于减少线程局部变量的存储需求。如果再次访问此线程局部变量,那么在默认情况下它将拥有其 initialValue。

void set(T value)
将此线程局部变量的当前线程副本中的值设置为指定值。许多应用程序不需要这项功能,它们只依赖于 initialValue() 方法来设置线程局部变量的值。

三、实例

package concurrent;

import java.util.concurrent.atomic.AtomicInteger;
public class ThreadLocalDemo {
      public static void main(String []args){
             for(int i=0;i<5;i++){
            final Thread t = new Thread(){
           @Override
           public void run(){
          System.out.println(“当前线程:”+Thread.currentThread().getName()+”已分配 ID:”+ThreadId.get());
        }
   };
         t.start();
   }
}
static class ThreadId{
//一个递增的序列,使用AtomicInger原子变量保证线程安全
      private static final AtomicInteger nextId = new AtomicInteger(0);
//线程本地变量,为每个线程关联一个唯一的序号
      private static final ThreadLocal<Integer> threadId =
      new ThreadLocal<Integer>() {
     @Override
    protected Integer initialValue() {
         //相当于nextId++,由于nextId++这种操作是个复合操作而非原子操作,会有线程            //安全问题(可能在初始化时就获取到相同的ID,所以使用原子变量
        return nextId.getAndIncrement();
  }
};
//返回当前线程的唯一的序列,如果第一次get,会先调用initialValue,后面看源码就了解了
       public static int get() {
          return threadId.get();
     }
   }
}
运行结果:
当前线程:Thread-4,已分配ID:1
当前线程:Thread-0,已分配ID:0
当前线程:Thread-2,已分配ID:3
当前线程:Thread-1,已分配ID:4
当前线程:Thread-3,已分配ID:2
这个程序通过妙用ThreadLocal,既实现多线程并发,游兼顾数据的安全性。

四、总结

ThreadLocal使用场合主要解决多线程中数据数据因并发产生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,单大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。

ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。

ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。

五、ThreadLocal使用的一般步骤

1、在多线程的类(如ThreadDemo类)中,创建一个ThreadLocal对象threadXxx,用来保存线程间需要隔离处理的对象xxx。
2、在ThreadDemo类中,创建一个获取要隔离访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null时候,应该new()一个隔离访问类型的对象,并强制转换为要应用的类型。
3、在ThreadDemo类的run()方法中,通过getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。

六、threadlocal内存泄漏
强引用:内存不足时,对象不会被GC回收,对象为null时,可被GC回收;
软引用:内存不足时,对象可以被GC回收;
弱引用:GC发现了,就会被回收,一旦我不需要某个引用,JVM会自动帮我处理它,这样我就不需要做其它操作;
虚引用:虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入ReferenceQueue中。而其它引用是被JVM回收后才被传入ReferenceQueue中的。由于这个机制,所以虚引用大多被用于引用销毁前的处理工作。同时,虚引用创建的时候,必须带有ReferenceQueue

ThreadLocal在ThreadLocalMap中是以一个弱引用身份被Entry中的Key引用的,因此如果ThreadLocal没有外部强引用来引用它,那么ThreadLocal会在下次JVM垃圾收集时被回收。这个时候就会出现Entry中Key已经被回收,出现一个null Key的情况,外部读取ThreadLocalMap中的元素是无法通过null Key来找到Value的。因此如果当前线程的生命周期很长,一直存在,那么其内部的ThreadLocalMap对象也一直生存下来,这些null key就存在一条强引用链的关系一直存在:Thread –> ThreadLocalMap–>Entry–>Value,这条强引用链会导致Entry不会回收,Value也不会回收,但Entry中的Key却已经被回收的情况,造成内存泄漏。但JVM团队已经考虑到这样的情况,并做了一些措施来保证ThreadLocal尽量不会内存泄漏:在ThreadLocal的get()、set()、remove()方法调用的时候会清除掉线程ThreadLocalMap中所有Entry中Key为null的Value,并将整个Entry设置为null,利于下次内存回收。

Git常用命令使用大全

git常用命令使用大全
查看、添加、提交、删除、找回,重置修改文件
git help <command> # 显示command的help
git show # 显示某次提交的内容 git show $id
git co — <file> # 抛弃工作区修改
git co . # 抛弃工作区修改
git add <file> # 将工作文件修改提交到本地暂存区
git add . # 将所有修改过的工作文件提交暂存区
git rm <file> # 从版本库中删除文件
git rm <file> –cached # 从版本库中删除文件,但不删除文件
git reset <file> # 从暂存区恢复到工作文件
git reset — . # 从暂存区恢复到工作文件
git reset –hard # 恢复最近一次提交过的状态,即放弃上次提交后的所有本次修改
git ci <file> git ci . git ci -a # 将 git add, git rm和 git ci等操作都合并在一起做 git ci -am “some comments”
git ci –amend # 修改最后一次提交记录
git revert <$id> # 恢复某次提交的状态,恢复动作本身也创建次提交对象
git revert HEAD # 恢复最后一次提交的状态
查看文件diff
git diff <file> # 比较当前文件和暂存区文件差异 git diff
git diff <id1><id1><id2> # 比较两次提交之间的差异
git diff <branch1>..<branch2> # 在两个分支之间比较
git diff –staged # 比较暂存区和版本库差异
git diff –cached # 比较暂存区和版本库差异
git diff –stat # 仅仅比较统计信息
查看提交记录
git log git log <file> # 查看该文件每次提交记录
git log -p <file> # 查看每次详细修改内容的diff
git log -p -2 # 查看最近两次详细修改内容的diff
git log –stat #查看提交统计信息
tig Mac上可以使用tig代替diff和log,brew install tig
git 本地分支管理
查看、切换、创建和删除分支
git br -r # 查看远程分支
git br <new_branch> # 创建新的分支
git br -v # 查看各个分支最后提交信息
git br –merged # 查看已经被合并到当前分支的分支
git br –no-merged # 查看尚未被合并到当前分支的分支
git co <branch> # 切换到某个分支
git co -b <new_branch> # 创建新的分支,并且切换过去
git co -b <new_branch> <branch> # 基于branch创建新的new_branch
git co $id # 把某次历史提交记录checkout出来,但无分支信息,切换到其他分支会自动删除
git co $id -b <new_branch> # 把某次历史提交记录checkout出来,创建成一个分支
git br -d <branch> # 删除某个分支
git br -D <branch> # 强制删除某个分支 (未被合并的分支被删除的时候需要强制)
分支合并和rebase
git merge <branch> # 将branch分支合并到当前分支
git merge origin/master –no-ff # 不要Fast-Foward合并,这样可以生成merge提交
git rebase master <branch> # 将master rebase到branch,相当于: git co <branch> && git rebase master && git co master && git merge <branch>
git补丁管理(方便在多台机器上开发同步时用)
git diff > ../sync.patch # 生成补丁
git apply ../sync.patch # 打补丁
git apply –check ../sync.patch #测试补丁能否成功
git暂存管理
git stash # 暂存
git stash list # 列所有stash
git stash apply # 恢复暂存的内容
git stash drop # 删除暂存区
git远程分支管理
git pull # 抓取远程仓库所有分支更新并合并到本地
git pull –no-ff # 抓取远程仓库所有分支更新并合并到本地,不要快进合并
git fetch origin # 抓取远程仓库更新
git merge origin/master # 将远程主分支合并到本地当前分支
git co –track origin/branch # 跟踪某个远程分支创建相应的本地分支
git co -b <local_branch> origin/<remote_branch> # 基于远程分支创建本地分支,功能同上
git push # push所有分支
git push origin master # 将本地主分支推到远程主分支
git push -u origin master # 将本地主分支推到远程(如无远程主分支则创建,用于初始化远程仓库)
git push origin <local_branch> # 创建远程分支, origin是远程仓库名
git push origin <local_branch>:<remote_branch> # 创建远程分支
git push origin :<remote_branch> #先删除本地分支( git br -d <branch>),然后再push删除远程分支
git远程仓库管理
gitHub
git remote -v # 查看远程服务器地址和仓库名称
git remote show origin # 查看远程服务器仓库状态
git remote add origin git@ github:robbin/robbin_site. git # 添加远程仓库地址
git remote set-url origin git@ github.com:robbin/robbin_site. git # 设置远程仓库地址(用于修改远程仓库地址) git remote rm <repository> # 删除远程仓库
创建远程仓库
git clone –bare robbin_site robbin_site. git # 用带版本的项目创建纯版本仓库
scp -r my_project. git git@ git.csdn.net:~ # 将纯仓库上传到服务器上
mkdir robbin_site. git && cd robbin_site. git && git –bare init # 在服务器创建纯仓库
git remote add origin git@ github.com:robbin/robbin_site. git # 设置远程仓库地址
git push -u origin master # 客户端首次提交
git push -u origin develop # 首次将本地develop分支提交到远程develop分支,并且track
git remote set-head origin master # 设置远程仓库的HEAD指向master分支
也可以命令设置跟踪远程库和本地库
git branch –set-upstream master origin/master
git branch –set-upstream develop origin/develop

转载:http://www.cnblogs.com/cspku/articles/Git_cmds.html
git init
在本地新建一个repo,进入一个项目目录,执行git init,会初始化一个repo,并在当前文件夹下创建一个.git文件夹.

git clone
获取一个url对应的远程Git repo, 创建一个local copy.
一般的格式是git clone [url].
clone下来的repo会以url最后一个斜线后面的名称命名,创建一个文件夹,如果想要指定特定的名称,可以git clone [url] newname指定.

git status
查询repo的状态.
git status -s: -s表示short, -s的输出标记会有两列,第一列是对staging区域而言,第二列是对working目录而言.

git log
show commit history of a branch.
git log –oneline –number: 每条log只显示一行,显示number条.
git log –oneline –graph:可以图形化地表示出分支合并历史.
git log branchname可以显示特定分支的log.
git log –oneline branch1 ^branch2,可以查看在分支1,却不在分支2中的提交.^表示排除这个分支(Window下可能要给^branch2加上引号).
git log –decorate会显示出tag信息.
git log –author=[author name] 可以指定作者的提交历史.
git log –since –before –until –after 根据提交时间筛选log.
–no-merges可以将merge的commits排除在外.
git log –grep 根据commit信息过滤log: git log –grep=keywords
默认情况下, git log –grep –author是OR的关系,即满足一条即被返回,如果你想让它们是AND的关系,可以加上–all-match的option.
git log -S: filter by introduced diff.
比如: git log -SmethodName (注意S和后面的词之间没有等号分隔).
git log -p: show patch introduced at each commit.
每一个提交都是一个快照(snapshot),Git会把每次提交的diff计算出来,作为一个patch显示给你看.
另一种方法是git show [SHA].
git log –stat: show diffstat of changes introduced at each commit.
同样是用来看改动的相对信息的,–stat比-p的输出更简单一些.

git add
在提交之前,Git有一个暂存区(staging area),可以放入新添加的文件或者加入新的改动. commit时提交的改动是上一次加入到staging area中的改动,而不是我们disk上的改动.
git add .
会递归地添加当前工作目录中的所有文件.

git diff
不加参数的git diff:
show diff of unstaged changes.
此命令比较的是工作目录中当前文件和暂存区域快照之间的差异,也就是修改之后还没有暂存起来的变化内容.

若要看已经暂存起来的文件和上次提交时的快照之间的差异,可以用:
git diff –cached 命令.
show diff of staged changes.
(Git 1.6.1 及更高版本还允许使用 git diff –staged,效果是相同的).

git diff HEAD
show diff of all staged or unstated changes.
也即比较woking directory和上次提交之间所有的改动.

如果想看自从某个版本之后都改动了什么,可以用:
git diff [version tag]
跟log命令一样,diff也可以加上–stat参数来简化输出.

git diff [branchA] [branchB]可以用来比较两个分支.
它实际上会返回一个由A到B的patch,不是我们想要的结果.
一般我们想要的结果是两个分支分开以后各自的改动都是什么,是由命令:
git diff [branchA]…[branchB]给出的.
实际上它是:git diff $(git merge-base [branchA] [branchB]) [branchB]的结果.

git commit
提交已经被add进来的改动.
git commit -m “the commit message”
git commit -a 会先把所有已经track的文件的改动add进来,然后提交(有点像svn的一次提交,不用先暂存). 对于没有track的文件,还是需要git add一下.
git commit –amend 增补提交. 会使用与当前提交节点相同的父节点进行一次新的提交,旧的提交将会被取消.

git reset
undo changes and commits.
这里的HEAD关键字指的是当前分支最末梢最新的一个提交.也就是版本库中该分支上的最新版本.
git reset HEAD: unstage files from index and reset pointer to HEAD
这个命令用来把不小心add进去的文件从staged状态取出来,可以单独针对某一个文件操作: git reset HEAD – – filename, 这个- – 也可以不加.
git reset –soft
move HEAD to specific commit reference, index and staging are untouched.
git reset –hard
unstage files AND undo any changes in the working directory since last commit.
使用git reset —hard HEAD进行reset,即上次提交之后,所有staged的改动和工作目录的改动都会消失,还原到上次提交的状态.
这里的HEAD可以被写成任何一次提交的SHA-1.
不带soft和hard参数的git reset,实际上带的是默认参数mixed.

总结:
git reset –mixed id,是将git的HEAD变了(也就是提交记录变了),但文件并没有改变,(也就是working tree并没有改变). 取消了commit和add的内容.
git reset –soft id. 实际上,是git reset –mixed id 后,又做了一次git add.即取消了commit的内容.
git reset –hard id.是将git的HEAD变了,文件也变了.
按改动范围排序如下:
soft (commit) < mixed (commit + add) < hard (commit + add + local working)

git revert
反转撤销提交.只要把出错的提交(commit)的名字(reference)作为参数传给命令就可以了.
git revert HEAD: 撤销最近的一个提交.
git revert会创建一个反向的新提交,可以通过参数-n来告诉Git先不要提交.

git rm
git rm file: 从staging区移除文件,同时也移除出工作目录.
git rm –cached: 从staging区移除文件,但留在工作目录中.
git rm –cached从功能上等同于git reset HEAD,清除了缓存区,但不动工作目录树.

git clean
git clean是从工作目录中移除没有track的文件.
通常的参数是git clean -df:
-d表示同时移除目录,-f表示force,因为在git的配置文件中, clean.requireForce=true,如果不加-f,clean将会拒绝执行.

git mv
git rm – – cached orig; mv orig new; git add new

git stash
把当前的改动压入一个栈.
git stash将会把当前目录和index中的所有改动(但不包括未track的文件)压入一个栈,然后留给你一个clean的工作状态,即处于上一次最新提交处.
git stash list会显示这个栈的list.
git stash apply:取出stash中的上一个项目(stash@{0}),并且应用于当前的工作目录.
也可以指定别的项目,比如git stash apply stash@{1}.
如果你在应用stash中项目的同时想要删除它,可以用git stash pop

删除stash中的项目:
git stash drop: 删除上一个,也可指定参数删除指定的一个项目.
git stash clear: 删除所有项目.

git branch
git branch可以用来列出分支,创建分支和删除分支.
git branch -v可以看见每一个分支的最后一次提交.
git branch: 列出本地所有分支,当前分支会被星号标示出.
git branch (branchname): 创建一个新的分支(当你用这种方式创建分支的时候,分支是基于你的上一次提交建立的).
git branch -d (branchname): 删除一个分支.
删除remote的分支:
git push (remote-name) :(branch-name): delete a remote branch.
这个是因为完整的命令形式是:
git push remote-name local-branch:remote-branch
而这里local-branch的部分为空,就意味着删除了remote-branch

git checkout
git checkout (branchname)

切换到一个分支.
git checkout -b (branchname): 创建并切换到新的分支.
这个命令是将git branch newbranch和git checkout newbranch合在一起的结果.
checkout还有另一个作用:替换本地改动:
git checkout –<filename>
此命令会使用HEAD中的最新内容替换掉你的工作目录中的文件.已添加到暂存区的改动以及新文件都不会受到影响.
注意:git checkout filename会删除该文件中所有没有暂存和提交的改动,这个操作是不可逆的.

git merge
把一个分支merge进当前的分支.
git merge [alias]/[branch]
把远程分支merge到当前分支.

如果出现冲突,需要手动修改,可以用git mergetool.
解决冲突的时候可以用到git diff,解决完之后用git add添加,即表示冲突已经被resolved.

git tag
tag a point in history as import.
会在一个提交上建立永久性的书签,通常是发布一个release版本或者ship了什么东西之后加tag.
比如: git tag v1.0
git tag -a v1.0, -a参数会允许你添加一些信息,即make an annotated tag.
当你运行git tag -a命令的时候,Git会打开一个编辑器让你输入tag信息.

我们可以利用commit SHA来给一个过去的提交打tag:
git tag -a v0.9 XXXX

push的时候是不包含tag的,如果想包含,可以在push时加上–tags参数.
fetch的时候,branch HEAD可以reach的tags是自动被fetch下来的, tags that aren’t reachable from branch heads will be skipped.如果想确保所有的tags都被包含进来,需要加上–tags选项.

git remote
list, add and delete remote repository aliases.
因为不需要每次都用完整的url,所以Git为每一个remote repo的url都建立一个别名,然后用git remote来管理这个list.
git remote: 列出remote aliases.
如果你clone一个project,Git会自动将原来的url添加进来,别名就叫做:origin.
git remote -v:可以看见每一个别名对应的实际url.
git remote add [alias] [url]: 添加一个新的remote repo.
git remote rm [alias]: 删除一个存在的remote alias.
git remote rename [old-alias] [new-alias]: 重命名.
git remote set-url [alias] [url]:更新url. 可以加上—push和fetch参数,为同一个别名set不同的存取地址.

git fetch
download new branches and data from a remote repository.
可以git fetch [alias]取某一个远程repo,也可以git fetch –all取到全部repo
fetch将会取到所有你本地没有的数据,所有取下来的分支可以被叫做remote branches,它们和本地分支一样(可以看diff,log等,也可以merge到其他分支),但是Git不允许你checkout到它们.

git pull
fetch from a remote repo and try to merge into the current branch.
pull == fetch + merge FETCH_HEAD
git pull会首先执行git fetch,然后执行git merge,把取来的分支的head merge到当前分支.这个merge操作会产生一个新的commit.
如果使用–rebase参数,它会执行git rebase来取代原来的git merge.

git rebase
–rebase不会产生合并的提交,它会将本地的所有提交临时保存为补丁(patch),放在”.git/rebase”目录中,然后将当前分支更新到最新的分支尖端,最后把保存的补丁应用到分支上.
rebase的过程中,也许会出现冲突,Git会停止rebase并让你解决冲突,在解决完冲突之后,用git add去更新这些内容,然后无需执行commit,只需要:
git rebase –continue就会继续打余下的补丁.
git rebase –abort将会终止rebase,当前分支将会回到rebase之前的状态.

git push
push your new branches and data to a remote repository.
git push [alias] [branch]
将会把当前分支merge到alias上的[branch]分支.如果分支已经存在,将会更新,如果不存在,将会添加这个分支.
如果有多个人向同一个remote repo push代码, Git会首先在你试图push的分支上运行git log,检查它的历史中是否能看到server上的branch现在的tip,如果本地历史中不能看到server的tip,说明本地的代码不是最新的,Git会拒绝你的push,让你先fetch,merge,之后再push,这样就保证了所有人的改动都会被考虑进来.

git reflog
git reflog是对reflog进行管理的命令,reflog是git用来记录引用变化的一种机制,比如记录分支的变化或者是HEAD引用的变化.
当git reflog不指定引用的时候,默认列出HEAD的reflog.
HEAD@{0}代表HEAD当前的值,HEAD@{3}代表HEAD在3次变化之前的值.
git会将变化记录到HEAD对应的reflog文件中,其路径为.git/logs/HEAD, 分支的reflog文件都放在.git/logs/refs目录下的子目录中.

特殊符号:
^代表父提交,当一个提交有多个父提交时,可以通过在^后面跟上一个数字,表示第几个父提交: ^相当于^1.
~<n>相当于连续的<n>个^.

转载:http://www.cnblogs.com/mengdd/p/4153773.html

IDEA常用快捷键

1. 查询快捷键
ctrl+shift+f 指定范围替换文本/全局替换
ctrl+shift+n/双击shift 查找文件
ctrl+shift+alt+n 查找类中的方法或变量
ctrl+f 在当前窗口查找文本
ctrl+r 在 当前窗口替换文本
ctrl+g 定位行
ctrl+shift+r 指定范围替换文本/全局替换
alt+shift+c 查找修改的文件
ctrl+e 最近打开的文件
ctrl+alt+f7 选中的字符查找工程出现的地方
ctrl+shift+o 弹出显示查找内容
ctrl+n 查找类
ctrl+alt+b 找所有的子类
ctrl+shift+b 找变量的类
f2 或shift+f2 高亮错误或警告快速定位
2.代码补全
ctrl+alt+space 类名或接口名提示
ctrl+j 自动代码
alt+insert 生成代码(如get,set方法,构造函数等)
ctrl+alt+o 优化导入的类和包
ctrl+shift+space 自动补全代码
ctrl+空格 代码提示
alt+回车 导入包,自动修正
ctrl+alt+l 格式化代码
ctrl+alt+i 自动缩进
ctrl+p 方法参数提示
ctrl+alt+t 把选中的代码放在 try{} if{} else{} 里
ctrl+e 最近更改的代码
ctrl+d 复制行
ctrl+x 剪切,删除行

3.其它常用快捷键
cirl+u 大小写切换
ctrl+z 撤回
ctrl+shift+z 撤回倒退
ctrl+alt+f12 资源管理器打开文件夹
alt+f1 查找文件所在目录位置
shift+alt+insert 竖编辑模式
ctrl+/ 注释//
ctrl+shift+/ 注释/*…*/
ctrl+w 代码快速选择,
ctrl+b 快速打开光标处的类或方法
alt+ ←/→ 切换代码视图
ctrl+alt ←/→ 返回上次编辑的位置
alt+ ↑/↓ 在代码块间快速定位(function)
shift+f6 重构-重命名
ctrl+h 显示类结构图
ctrl+q 显示注释文档
alt+1 快速打开或隐藏工程面板
ctrl+shift+↑ 代码上方用空行,代码上移
ctrl+shift+↓ 代码上方用空行,代码下移
ctrl+↑ 窗口上移
ctrl+↓ 窗口下移
ctrl+f4 关闭窗口