2013年11月25日 星期一

Git (二) Rebase vs Merge

rebase 跟 merge 做的事情其實很像

主要的差別在於
假設我在 branch下指令  git merge master,
merge 會一次性的將所有master的commit 直接merge進 branch
rebase的話  則會分成多次commit 進來




舉個例子來看看


上面分別有master and branch 兩個分支
我在 branch 上做了兩次commit  branchC  and branchD
然後我在 master 上做了三次commit masterA ,masterB and masterC



Merge

首先我們先使用merge 指令
在branch分支下指令 git merge master
master的資料將會在一次commit 中將 masterA , masterB and masterC
都加進我的branch分支內,我們看到的commit結果會是如上圖
branchC, branchD ,Merge branch 'master' into branch ,
要倒回去的話只需要下指令git reset --hard HEAD^
就可以恢復merge之前的狀態



Rebase 

接著使用 rebase 指令
在branch分支下指令 git rebase master



可以看到原本分為兩個的分支結合再一起了
可以看到git rebase master之後的結果會是  
masterA , masterB, masterC , branchC and branchD 跟git merge master的結果有些差別
不過上圖並不好解釋會何會變成這樣,下面的圖比較能解釋他的原因

原本的基準點為左圖最下的藍色點,
當我們使用 git rebase master時,branch分支會將基準點從最下方的藍色點rebase到 master 的最上方的點,也就是如右圖所示(可以把他想成pointer 將指標指過去的感覺),
所以最後會只看到一條線。
經過rebase之後的分支要倒回並不是那麼的容易,
我的做法會是在create一個新的分支,然後將這次的Rebase都倒回之後再用cherry-pick將原本的commit 拉回來。



結論

            git merge master是一次性的commit ,然而git rebase master是多次性的commit 。


git merge 的commit是依照順序的 branchC, branchD 然後將master commit 進來masterA masterB masterC,
所以結果會是:branchC, branchD masterA masterB masterC
然而git rebase 的commit是先rebase到master分支再將原本的commit apply回來
所以順序會是: masterA masterB masterC branchC, branchD


Merge和Rebase的用途也不太相同

通常Rebase的用途在於  有同事在遠端commit了新的code進來
我希望我現在修改的版本能base到最新的版本
這時候就可以使用rebase command 讓手邊的程式更新到最新的版本

Merge的話比較像是我在某個分支完成了一個大型修正或功能,而且也經過大量測試之後
想要將這個功能導入我現在的working directory,
這時候就可以使用merge, 記住 merge是一次性的commit 所以所有關於該feature的功能將會一次性的導入你並不會在working directory中看到有關於該feature的其他commit




2013年11月24日 星期日

Git (一) git reset

今天要談的是git  reset的功能, git reset有分三個選項

git reset --soft
git reset --mixed 也就是 git reset
git reset --hard

首先我們要分成三個部分說明






當我們新增了一個 hello.txt 檔 到 working directory之後,
我們希望將該file commit 進  git directory,
首先我們必須下指令 git add hello.txt , 將我們希望更改的檔案放進 staging area,
接著再下指令  git commit -m "add hello"     ("add hello" 為 commit的文字描述),
然後資料就會進到git directory了。
                     
                                            太好了所有的修改都加進了git directory 我可以收工了。

可惜....  事與願違,當初做 add hello 這個修改時,我除了修改了這個檔案 hello.txt之外,我還修改了world.txt這個檔案。
     
                            糟了,我commit出了差錯老闆知道的話一定會把我臭罵一頓。

還好git 有支援倒回的指令,
我們只需要下指令 git reset --soft HEAD^ 就會將這次的commit 取消,
然後我們只需要再下  git add world.txt -> git commit -m "add hello"  ,這樣就神不知鬼不覺的將資料加進這次commit了。

當然我們也可以下指令 git reset --HEAD^ ,直接取消掉 commit 以及 staging area , 回到 working directory ,這個階段所有的資料都尚未被加入 staging area,
然後接著下指令 git add hello.txt -> git add world.txt -> git commit -m "add hello"
就好了。

然後值得注意的是 git reset HEAD^git reset --hard HEAD^ 的差別在於
git reset --hard HEAD^ 會將該次的修改也一併倒回,也就是當我下了這個指令之後
hello.txt and world.txt將回到上一次commit 的版本 (這次所做的所有修改也都會消失)

                            除非你知道正在做甚麼,
                                 否則不要輕易下這個指令,不然辛苦修改的成果可能就在瞬間被銷毀。

git reset 也可以用於 fast forward 只需要知道想要fast forward的SHA
使用 git reset --hard xxxxxx        xxxxxx 為 head的SHA
這樣就會使分支跳到指定的位置

2013年11月23日 星期六

[UML] 物件導向分析與設計

介紹

今天花了點時間看了物件導向分析與設計,看了前四章的感覺是,這本書花了很多時間在指導我們如何分析客戶的需求,如何寫出良好的 user cases ,當然對於程式的封裝也有稍微提及。
不過今天要介紹的是UML圖




UML
         
             是系統分析以及設計的重要產物,也是實作以及測試的依據。

下面這張圖是 head first 物件導向分析與設計裡面的一個 sample,
使用者希望能有一個 Remote 能夠在按下按鈕的時候, DogDoor 會打開/關閉,
然後也希望家裡的狗 Bark 的時候, DogDoor 可以 Recognize 是自己家的狗然後開門。


上圖中  Remote -> DogDoor 之間的數字  1 代表著  
Remote has 1 個 DogDoor
相同的  BarkRecognizer also has 1個 DogDoor
DogDoor has n 個 Bark  而DogDoor 可以在 allowedBarks的屬性裡儲存任意數量的Bark


看完了UML圖以及解釋之後接下來就是實作部分



Sample Code


Bark.java
  
public class Bark {

 /**
  * @param args
  */
 private String sound;
 public Bark(String sound)
 {
  this.sound = sound;
 }
 public String getSound()
 {
  return sound;
 }
 public boolean equals(Bark bark)
 {
  if(bark.getSound().equals(this.sound))
   return true;
  return false;
 }
} 


BarkRecognizer.java

public class BarkRecognizer {

 /**
  * @param args
  */
 private DogDoor door;
 public BarkRecognizer(DogDoor door)
 {
  this.door = door;
 }
 public void recognize(Bark bark)
 {
  java.util.List<Bark> allowedBarks = door.getAllowedBarks();
  for (Bark allowedBark : allowedBarks) {
   if(bark.equals(allowedBark))
   {
    door.Open();
    return;
   }
  }
  System.out.println("Door is not Open");
 }
}

DogDoor.java

public class DogDoor {

 /**
  * @param args
  */
 private List<Bark> allowedBarks = new ArrayList<Bark>();   
 private boolean Open=false;
 public void Open()
 {
  System.out.println("Door Open");
  final Timer timer = new Timer();
  timer.schedule(new TimerTask(){

   @Override
   public void run() {
    // TODO Auto-generated method stub
    Close();
    timer.cancel();
   }
   
  },5000);
 }
 public void Close()
 {
  System.out.println("Door Close");
 }
 public boolean IsOpen()
 {
  return Open; 
 }
 public void addAllowedBark(Bark bark)
 {
  allowedBarks.add(bark);
 }
 public List<Bark> getAllowedBarks()
 {
  return allowedBarks;
 }
}

Remote.java

public class Remote {

 /**
  * @param args
  */
 private DogDoor door;
 public Remote(DogDoor door)
 {
  this.door = door;
 }
 public void pressButton()
 {
  if(door.IsOpen())
   door.Close();
  door.Open();
 }
}

接著拿出head first 裡面所提供的測試檔案


Test



 public static void main(String[] args) {
  // TODO Auto-generated method stub
  DogDoor door = new DogDoor();
  door.addAllowedBark(new Bark("rowlf"));
  door.addAllowedBark(new Bark("rooowlf"));
  door.addAllowedBark(new Bark("rawlf"));
  door.addAllowedBark(new Bark("woof"));
  BarkRecognizer recognizer = new BarkRecognizer(door);
  Remote remote = new Remote(door);
  
  System.out.println("Bruce starts barking.");
  recognizer.recognize(new Bark("rowlf"));
  
  System.out.println("Bruce has gone outside...");
  
  try{
   Thread.currentThread().sleep(6000);
  }catch(InterruptedException ex)
  {
   
  }
  
  System.out.println("Bruce's all done...");
  System.out.println("but he's stuck outside...");
  
  Bark smallDogBark = new Bark("open the door");
  System.out.println("A small dog starts barking.");
  recognizer.recognize(smallDogBark);
  
  System.out.println("Bruce starts barking");
  recognizer.recognize(new Bark("rowlf"));
  System.out.println("Bruce's Back inside");
  
 }


Result

Bruce starts barking.
Door Open
Bruce has gone outside...
Door Close
Bruce's all done...
but he's stuck outside...
A small dog starts barking.
Door is not Open
Bruce starts barking
Door Open
Bruce's Back inside

Door Close



結論

這次展示了如何從UML 到程式的coding
UML就有點像是蓋房子之前的藍圖,
有了藍圖就可以降低user想蓋巴黎鐵塔結果developer卻蓋出金字塔的問題。
我想這對user 來說應該也算是一大福音吧 XD



2013年11月20日 星期三

CleanCode 讀書筆記 (六) 用程式說故事

命名是個藝術


clean code裡面有提到,越多的註解代表程式寫得越差。
最近一直在體會這句話的涵意,讓我們來看看下面的例子:

            
            bool bo = Dic.TryGetValue("hello",out val);
            if (bo)
                Console.WriteLine("do something");




從上面的例子我們知道當bo為true時,我們必須 do something。
再看看下面的例子:
            
            bool HasKey = Dic.TryGetValue("hello",out val);
            if (HasKey)
                Console.WriteLine("do something");

上面的例子讓我們感覺像是在讀文章

first, we get a variable HasKey and then if  it hsa key  we will do something.

接著我們再看一段簡單的程式:
  
            int x = 0;
            int y = 0;
            int z =0;
            while (z < 3)
            {
                if (IsTrue())
                    x++;
                else
                    y++;

                if (x == 3)
                    x_event();
                if (y == 4)
                {
                    y_event();
                }
            }

我是故意寫那麼醜的,我相信大多數的人看完都只有一個感想  WhatThe....
接著我們再看看修改過的程式:


            static int Three_People_Out =3;
            int Strike = 0;           
            int ball = 0;
            int out_People =0;
            while (out_People < Three_People_Out)
            {
                if (IsInStrikeZone())
                    Strike++;
                else
                    ball++;

                if (Strike == 3)
                    StrikeOut();
                if (ball == 4)
                {
                    base_on_balls();
                }
            }

相信看到修改過的程式應該可以看出這是在寫有關棒球的程式。

if IsInStrikeZone 則 好球++ 否則壞球++
if 三好球  則 StrikeOut
if 四壞球  則 保送 ...

善用命名可以讓我們不需要使用大量的註解,卻可以寫出可讀性高的程式。
命名真是一門藝術!!

2013年11月9日 星期六

[SourceCodeControl] Bitbucket 介紹

只要是開發project都需要做版本控管還有Issue Tracking,目前用過的版控工具有 SVN 和 Git
兩個的差異主要是:集中式管理以及分散式管理的差別。



使用上的差異

用了Git 之後就可以很快發現他的便利性,
例如: cherry-pick  可以將一次commit所做的變更快速的套至其他的Branch 甚至是project,
以及  Git可以在local端自行開發,然後可以只push部分的資料到remote端,
也就是說local 跟 remote 可以是不同的線,但是SVN的話就不行,server跟local是同一條線。


使用Git

既然要使用Git那就必須有remote端 網路上常常會聽到有人使用 GitHub,他是免費的server  但是免費的版本只能創建 public 的 repository 這讓我有點困擾。
有些project我希望他不是公開的,這個原因讓我去搜尋了其他可用的網站。


BitBucket

跟GitHub一樣提供remote端,但是BitBucket額外提供了  private 的 repository
雖然project的人數有限制,但對於小團隊來說是足夠的 ,並且他還提供了Issue Tracking的工具


以及類似Agile的任務領取指派介面



對我來說這樣的功能算是非常的充裕,最後....讓我們開始開發吧!! XD

[UI] 挖洞初體驗

最近跟Design Team的要了一些工具  然後開始嘗試設計

這次嘗試的是如何挖洞....

以下是成品


這個洞挖了我兩個多小時.....

感覺還有很大的進步空間  

2013年11月6日 星期三

Android 開發 (六) Fragment replace

Fragment 能使我們在多個頁面之間做切換
以下是簡單的範例

MainActivity.java

public class MainActivity extends Activity 
 implements myFragment.OnClickListener{

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  myFragment newFragment = new myFragment();
        FragmentTransaction transaction = this.getFragmentManager().beginTransaction();

        transaction.add(R.id.rela, newFragment, "first");
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        transaction.commit();
 }
    
 public void onClick()
 {
  Log.d("Ted","what");
  NxtFragment newFragment = new NxtFragment();
        FragmentTransaction transaction = this.getFragmentManager().beginTransaction();
                     
        transaction.replace(R.id.rela, newFragment);
        transaction.addToBackStack(null);
        transaction.commit();
 }


首先 我們在myFragment的class 裡面製做了一個介面 myFragment.OnClickListener
當click event被觸發時 來呼叫切換的動作

在onCreate裡 我們將myFragment 加入至 Main page 裡

onClick 當click事件被觸發時 將 Main page裡的 fragment做更換


myFragment.java


public class myFragment extends Fragment {
 OnClickListener mCallback;

    
    public interface OnClickListener {       
        public void onClick();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        try {
            mCallback = (OnClickListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnClickListener");
        }
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_layout, container, false);
        Button btn = (Button)v.findViewById(R.id.ClickBtn);
        btn.setOnClickListener(new Button.OnClickListener()
        {

   @Override
   public void onClick(View arg0) {
    // TODO Auto-generated method stub
    mCallback.onClick();
   }         
        });
        return v;
    }

在onAttach 裡將 MainActivity 傳給 myFragment

使 myFragment能在 click event被觸發時能呼叫 onClick 的method 來做換頁的動作


 以下是這個範例的示範圖

這是第一頁  當按下 Click me 之後會跳至第二頁

 按下之後的結果


小結:

1.Fragment 使 Developer在製作手機與平板之間的UI時  相對上較便利
如上範例所示兩個fragment之間的切換 
假設在平板上一個是在左邊一個是在右邊  在手機上變成第一頁另一個變成第二頁
用fragment 只需要判斷  是要將fragment add到 R.id.rela (平板)  還是  replace掉(手機)

2.Fragment 支援  Back的功能  這跟windows metro 的 navigate back功能蠻像的
就是回到前一頁

2013年11月5日 星期二

Android 開發 (五) 如何讀取圖片

利用以下的程式碼能將圖片讀取出來

 public void GetImage()
 {
  String[] projection = {MediaStore.Images.Thumbnails.DATA};
  
 
      Cursor cursor = this.getContentResolver().query( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
          projection, 
          null,      
          null,
          null);
  

  int columnIndex = cursor.getColumnIndex(MediaStore.Images.Thumbnails.DATA);
  for(int i =0;i<cursor.getCount();i++){
      cursor.moveToPosition(i);
 
     
      Bitmap bm = BitmapFactory.decodeFile(cursor.getString(columnIndex));
      map.put("img",  bm );
  }
  cursor.close();
 }



接著只需要將 map裡的資料 餵給imageview 即可

2013年11月1日 星期五

[Other] 如何將 Syntaxhighlighter 到blog裡

最近在思考如何在blog裡用較明顯的方式顯示程式
所以找到了 Syntaxhighlighter
首先先找到範本 -> 點擊編輯Html



接著使用搜尋找到</head>

接著在</head>之上貼入以下的文字

<!-- SyntaxHighlighter -->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css" />
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeEmacs.css" rel="stylesheet" type="text/css" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript" />

<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushAS3.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushColdFusion.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushDelphi.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushDiff.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushErlang.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushGroovy.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJavaFX.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPerl.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPlain.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPowerShell.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushRuby.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushScala.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushVb.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js" type="text/javascript" />
 
<script language="javascript" type="text/javascript">
 SyntaxHighlighter.config.bloggerMode = true;
 SyntaxHighlighter.all();
</script>
<!-- SyntaxHighlighter END -->




其中前兩個shCore 還有  shThemeEmcs 是可以做替換的
可以將圖片中的任意一個名稱輸入做替換
接著只需要在編寫文章的時候加入  
<pre class="brush:java;">....程式們 </pre>
就可以了