Google Code Prettify

2015年12月22日 星期二

spring db connection (xml)

  • JNDI
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName">
        <value>java:comp/env/jdbc/xxxxx</value>
    </property>
    <property name="lookupOnStartup" value="false"/>
    <property name="cache" value="true"/>
    <property name="proxyInterface" value="javax.sql.DataSource"/>
</bean>
  • JDBC
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close"
    p:driverClassName="${jdbc.driverClassName}"
    p:url="${jdbc.url}"
    p:username="${jdbc.username}"
    p:password="${jdbc.password}" />
上面是 spring framework 中設定資料庫連線的兩種方式,這兩種方式有什麼差別?
  1. JNDI 需配合在 AP Server 設定,也就是會造成對 AP Server 的相依性,JDBC 的設定方式就沒有這個缺點。在維護程式時用第二種方式,所有程式含設定都在同一個目錄下,管理非常容易,重新建置環境也容易。
  2. JNDI 提供 connection pool,JDBC 則沒有! 所以,在營運的正式環境上,一般不會採用 JDBC 的方式,因為太沒效率了! JDBC 的設定方式也有它適用的地方,就是用在單元測試! 在進行單元測試時,AP Server 通常是沒有啟動,沒辦法透過 JNDI 的方式建立資料庫連線,JDBC 的方式就派上用場了!
要同時滿足以上兩種方法的優點,不被 AP Server 束縛,又有 connection pool,可以如下設定: (下載)
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
    p:driverClassName="${jdbc.driverClassName}"
    p:url="${jdbc.url}"
    p:username="${jdbc.username}"
    p:password="${jdbc.password}"
/>



2015年11月8日 星期日

web development

項目日期
*
  Environment
1 Tomcat 7 安裝 SSL 憑證
2014/08/30
2 install GlassFish Server
2015/06/22
3 在 WebSphere 8.5.5.x 上遇到的 jar 檔版本和衝突問題
2017/03/30
*
  Programming (front-end)
1 struts2 jQuery tag (1) - TabbedPanel & DatePicker
2014/10/18
2 struts2 jQuery tag (2) - Grid
2014/10/19
3 jQuery: 處理 json 資料
2016/05/11
4 看見 Angular 的車尾燈
2017/03/26
5 第一支 Angular 程式
2017/04/06
6 在 WebSphere 上 Angular 中文存入資料庫變亂碼!
2017/06/20
7 Updating Angular CLI
2017/07/06
8 Spring Boot Angular Websocket
2018/07/20
*
  Programming (back end)
1 spring 4.x + struts 2.x + mybatis 3.x: getting started
2014/10/11
2 unit test on spring + struts2
2015/07/02
3 spring db connection
2015/12/22
4 spring MVC: getting started
2016/04/30
5 spring MVC + jQuery ajax
2016/05/02
6 spring MVC: 在 view 和 controller 間傳遞多列資料
2016/06/19
7 Spring Application Event
2017/07/23
8 變更 property 檔裡的設定值
2017/07/27
9 第一個 spring boot web 程式 (使用 embedded tomcat)
2017/12/31
10 application.properties@spring boot
2018/06/03
11 web server (https)
2023/06/11
*
  Programming (web services)
1 CXF - getting started
2016/07/04
2 spring boot RESTful 程式的單元測試
2018/03/23

2015年11月6日 星期五

重構: Duplicated Code (重複的程式碼)

Martin Fowler 大師於將近 20 年前出版的名著「Refactoring: Improving The Design of Existing Code」(重構 - 改善既有程式的設計),提出許多重構的手法,這些方法在當時是相當新穎的觀念與技術,現在已經是許多程式員每天寫程式一定會用到的。

在書中 3.1 節是談論重複的程式碼,這當然是程式的壞味道,是重構的標的物,底下將舉出我實際遇到的例子,以及我在重重限制下進行的重構。

這是一個有上百個 tables 的資料庫 web 系統,用 java 開發,沒有採用任何 MVC 的 framework,程式也沒有將 view、model 等區隔開,所以整個系統絕大多數的程式碼是寫在 jsp 裡,在這些 jsp 裡會看到大量的 html、javascript、jquery、css、java 摻雜,連接資料庫直接使用 JDBC API,底下的例子就是一個將 JDBC API 相關的程式,經過重構減少重複程式碼的例子。
PreparedStatement pstmt;
String sql;

sql = "UPDATE  FoudItem "  
    + " SET "
    + " name = NVL(?,' '),"
    + " type = ?, "
    + " rate = ? , "
    + " empNo = NVL(?,' '), "
    + "    mtime = SYSTIMESTAMP "
    + " WHERE fundNo =  RPAD(?,3) " 
    ;

pstmt = conn.prepareStatement(sql);
pstmt.setString(1, fundName);
pstmt.setString(2, fundType);
pstmt.setDouble(3, rate);
pstmt.setString(4, empNo);
pstmt.setString(5, fundNo);
pstmt.executeUpdate();
像這樣存取資料庫的程式碼在整個系統中有數百個,全部是同樣邏輯,先有一段用 string 一直相加產生的 sql statement,在產生 PreparedStatement 後,傳入參數並執行。我是後續接手維護的人,也被告知不能動大架構,所以我只簡單的如下重構,以去除重複的程式碼。




  • 將 sql statement 移到 xml 檔
<?xml version="1.0" encoding="UTF-8"?>

<sql>
    <statement id="update">
    <![CDATA[
    UPDATE  FoudItem "  
    SET "
        name = NVL(?,' '),"
        type = ?, "
        rate = ? , "
        empno = NVL(?,' '), "
        mtime = SYSTIMESTAMP "
    WHERE fundno =  RPAD(?,3)
    ]]>
    </statement>
</sql>
  • 自 xml 中讀取 sql statement
private static String getSqlStatement(String name, String id) {
        File file = new File(path + name + ".xml");

        SAXReader reader = new SAXReader();
        Document document;
        
        try {
            document = reader.read(file);
            Element sqlElmt = document.getRootElement();
            Iterator<Element> iter = sqlElmt.elementIterator("statement");
            while (iter.hasNext()) {
                Element elmt = iter.next();
                if (id.equals(elmt.attributeValue("id"))) {
                    return elmt.getText();
                }
            }
        } catch (DocumentException e) {
            logger.error(e.getMessage(), e);
        }
        
        return StringUtils.EMPTY;
    }
這段程式碼很簡單的讀取指定目錄下 xml 檔,以取得程式需要的 sql statement。
  • 傳入參數到 PreparedStatement 並執行
public static int execute(Connection conn, String name, String id, Object... params) throws SQLException {
        String sql = getSqlStatement(name, id);
        PreparedStatement pstmt = conn.prepareStatement(sql);        
        for(int i=1; i<=params.length; i++) {
            Object param = params[i-1];
            
            if (param instanceof String) {
                pstmt.setString(i, (String) param);
            }
            else if (param instanceof Long) {
                pstmt.setLong(i, (Long) param);
            }
            else if (param instanceof Double) {
                pstmt.setDouble(i, (Double) param);
            }
            else if (param instanceof BigDecimal) {
                pstmt.setBigDecimal(i, (BigDecimal) param);
            }
            else {
                throw new SQLException("field's type is invalid");
            }
        }
        
        return pstmt.execute();
    }
  • 改寫原 JDBC 相關的程式
上面的三個片斷,除了存放 sql 的 xml 檔,每次新增程式都要增加外,另外兩個片斷是共用程式,這些共用程式放在命名為 SQLExecutor 的類別裡,利用這些程式改寫原本的程式如下: 
SQLExecutor.execute(
    conn, "fund", "update",
    fundName, fundType, rate, empNo, fundNo
);
改寫完了之後,一大段的程式最後只剩一行指令! (從命名為 fund.xml 的檔案裡,讀取 id = update 的 sql statement,產生 PreparedStatement 後,將相關參數傳入執行。)

底下是重構一書 3.1 節的原文,有興趣的可以看看,沒看過這本書的人,建議買一本,絕對值得!
Duplicated Code
Number one in the stink parade is duplicated code. If you see the same code structure in more than one place, you can be sure that your program will be better if you find a way to unify them. 
The simplest duplicated code problem is when you have the same expression in two methods of the same class. Then all you have to do is Extract Method and invoke the code from both places. 
Another common duplication problem is when you have the same expression in two sibling subclasses. You can eliminate this duplication by using Extract Method in both classes then Pull Up Field. If the code is similar but not the same, you need to use Extract Method to separate the similar bits from the different bits. You may then find you can use Form Template Method. If the methods do the same thing with a different algorithm, you can choose the clearer of the two algorithms and use Substitute Algorithm. 
If you have duplicated code in two unrelated classes, consider using Extract Class in one class and then use the new component in the other. Another possibility is that the method really belongs only in one of the classes and should be invoked by the other class or that the method belongs in a third class that should be referred to by both of the original classes. You have to decide where the method makes sense and ensure it is there and nowhere else.

2015年10月25日 星期日

陽春版內容管理系統

台灣大約 14、15 年前開始引進內容管理系統,一開始最有名的產品是 TeamSite,聽說當時市佔也是世界第一。後來微軟推出 Share Point Server,也是內容管理的產品,但是,兩個產品真的很不一樣,適用的領域也很不一樣。

約 6、7 年前,有出現一款本土的內容管理產品,架構大略如下:
當時他們是以 subversion 來當版本的控管,如果是現在,應該會如上圖改用 Git 吧? 不知道是否產品太陽春,似乎推展的不順利,或是我太孤陋寡聞,只知道我當時任職的公司有買。

我認為這個陽春版的內容管理系統,在一些公司的開發團隊,應該都可以很快的自行開發出來。像是一些電子商務的公司,就可以在傳統的網站架構中,多一台 Git server,這個沒什麼太大成本,卻可以為網站的許多內容進行版本的控管,方便管理員回復特定內容到指定的版本。





Linux / Unix

項目日期
*
  Environment
1scientific linux 6.x 下如何播放音樂和影片?
2014/07/27
2在 scientific linux 上安裝 telnet & ssh service
2014/12/21
3在 ubuntu 上安裝 telnet service
2014/12/29
4Ubuntu 上的 PPPoE 設定
2020/05/01
5install GlassFish Server
2015/06/22
6第一次使用 MySQL
2014/10/10
7ssh 公鑰認證登入
2015/02/16
8在 linux 中無法切換身份? (su: Permission denied)
2017/12/22
9install Redis
2018/07/21
10RHEL8 無法安裝套件 No match for argument: xxx
2022/03/27
11install nfs server@Ubuntu
2023/11/08
*
  Shell Scripting
1使用 sort、join 處理 csv 檔案
2015/01/16
2EBCDIC <=> ASCII 間的轉換
2015/01/17
*
  C Programming
1在 linux 下寫 C 程式 (使用 eclipse/CDT)
2015/05/21
2Linux Network Programming - 初體驗
2015/05/23
3byte ordering functions
2015/05/26
4fork Function
2015/06/06

2015年10月23日 星期五

Tibco RV / JMS / Apache Kafka

項目日期
1Tibco RV request/reply 的同步與非同步
2015/02/07
2Tibco RV 的 Queue
2015/05/05
3Tibco RV - fault tolerance
2015/07/10
4JMS: getting started
2015/08/30
5JMS: 訊息的標頭、屬性和種類
2015/08/30
6install Kafka (single node)
2018/08/11

Git / Gradle

項目日期
*
  Git
1安裝最新的 git
2015/02/11
2建立一個 remote git bare repository
2015/02/24
3使用 TortoiseGit 連上 remote bare repository
2015/02/28
4JGit - getting started
2015/10/21
5JGit - 基本命令
2015/10/22
6JGit - 走訪、差異比對、日誌
2015/10/23
*
  Gradle
1Gradle: task
2018/02/23
2Gradle: 編譯、測試 web 專案
2017/08/10
3Gradle: 編譯 spring boot application
2018/02/26
*
  Azure DevOps
1Azure DevOps: CI Tutorial
2022/12/20


JGit - 走訪、差異比對、日誌



這是 git 各個階段版本控管的概念圖,非常重要! 雖然會想了解 JGit 的人,一定對這些都很熟了,但是還是貼出來大家看一下,因為這和接下來要談的差異比對有關。

首先看一下我的環境,如下圖所示,分了幾個 branch,故意弄的有點亂是為了測試。
要走訪 git repository,要先確定是要走訪那個 branch? 及要走訪的節點,一般來說,都是走訪 commit 或 tag 所在的節點。當然更詳細一點點的如下圖: (這些圖都是 TortoiseGit 產生的)。
這上面的每個節點的 name 和 object id,是非常重要的,要找到節點,就靠這些了! 下面是這篇要說明的程式,它會走訪被版控的檔案、目錄、並進行差異比對。 前兩篇說明過的。這篇不會重複,如果有疑問請翻閱前兩篇。
 1 public class DiffTest {
 2 
 3     @Before
 4     public void setUp() throws Exception {
 5     }
 6 
 7     @After
 8     public void tearDown() throws Exception {
 9     }
10 
11     //@Test
12     public void testDiff() throws Exception {
13         Repository repo = new FileRepositoryBuilder()
14         .setGitDir(new File("D:/TestJGit/local/.git"))
15         .build();
16         
17         RevTree headTree = getRevTree(repo, "refs/heads/master");
18         RevTree branchTree = getRevTree(repo, "refs/heads/branch_001");
19 
20         ObjectReader reader = repo.newObjectReader();
21         List<DiffEntry> listDiffs = compareTree(repo, reader, headTree,    branchTree);
22         
23         for (DiffEntry diff : listDiffs) {
24             System.out.println(diff);
25             FileMode mode = diff.getNewMode();
26             System.out.println(mode.toString());
27         }
28         
29         repo.close();
30     }
31     
32     @Test
33     public void testWalk() throws Exception {
34         Repository repo = new FileRepositoryBuilder()
35         .setGitDir(new File("D:/TestJGit/local/.git"))
36         .build();
37         
38         RevTree headTree = getRevTree(repo, "refs/heads/branch_003");
39         
40         try (TreeWalk treeWalk = new TreeWalk(repo)) {
41             treeWalk.addTree(headTree);
42             treeWalk.setRecursive(false);
43             
44             while (treeWalk.next()) {
45                 String rawPath = new String(treeWalk.getPathString());
46                 FileMode mode = treeWalk.getFileMode(0);
47                 System.out.print("rawPath = " + rawPath);
48                 if ((mode.getBits() & FileMode.TYPE_FILE) != 0) {
49                     System.out.println(" -- file");
50                 }
51                 if ((mode.getBits() & FileMode.TYPE_TREE) != 0) {
52                     System.out.println(" -- folder");
53                 }
54                 System.out.println(mode.toString());
55                 System.out.println(mode.getBits());
56                 System.out.println(treeWalk.getNameString());
57             }
58         }
59     }
60 
61     private List<DiffEntry> compareTree(Repository repo, ObjectReader reader,
62             RevTree newTree, RevTree oldTree)
63             throws IncorrectObjectTypeException, IOException, GitAPIException {
64         
65         CanonicalTreeParser newTreeIter = new CanonicalTreeParser();
66         newTreeIter.resetRoot(reader, newTree.getId());
67         
68         CanonicalTreeParser oldTreeIter = new CanonicalTreeParser();
69         oldTreeIter.resetRoot(reader, oldTree.getId());
70         
71         Git git = new Git(repo);
72         List<DiffEntry> listDiffs = git.diff().setOldTree(oldTreeIter).setNewTree(newTreeIter).call();
73         return listDiffs;
74     }
75 
76     private RevTree getRevTree(Repository repo, String objectName)
77             throws AmbiguousObjectException, IncorrectObjectTypeException,
78             IOException, MissingObjectException {
79         
80         RevTree tree = null;
81         try (RevWalk revWalk = new RevWalk(repo)) {
82             ObjectId objId = repo.resolve(objectName);
83             RevCommit commit = revWalk.parseCommit(objId);
84             tree = commit.getTree();
85         }
86         
87         return tree;
88     }
89 }
  • 先找到樹頭: getRevTree() method 就是用 object name (revision string) 去找到樹頭! 像是程式 17、18 行,我們要比對的是 master 和 branch_003 間的差異,就是先把這兩個節點的 object name 傳入 getRevTree() 以取得樹頭。JGit 的觀念多半是,要做什麼事,就會有個相對應的類別,這裡提供的就是 RevWalk 這個類別。簡單說明一下這個 method 的運作。
  1. 程式第 82 行,resolve() method 可以由 object name 取得 object id,因為很多 method 要的參數是 object id。 
  2. 程式第 83 行,RevWalk 的 parseCommit() 可以根據傳入的 object id 得到參考到 commit 那個節點的物件。 
  3. 由 RevCommit 的 getTree() 即可取得那個 commit 時的所有目錄、檔案,當然這些檔案的內容也是那個當下的內容。
  • 差異比對: 在 17、18 行取得檔案的樹狀結構後,在 21 行透過 compareTree() 比對,結果會以 List<DiffEntry> 傳回,這個 method 的寫法我還不太懂,先不解釋。
  • TreeWalk (第 40 行) 是 JGit 提供用來走訪檔案樹狀結構的類別,只要將檔案樹狀結構傳入 (第 41 行) 就可以開始走訪各個節點,setRecursive 傳入 false 就只會走訪最上層,要完整的走訪整棵樹就傳入 true。
  • 每個節點都可以取得它的 FileMode,這是記錄該節點基本屬性的物件,像是第 48、51 行所顯示的,呼叫 getBits() 後,傳回的整數其實是每個 bit 有它的意義,可以透過遮罩的方式取得各個 bit 的值,上面的程式就示範了判斷該節點是檔案或路徑的方法。
  • FileMode 的 toString() 在 API Document 裡雖然有說明,僅適合用在 debug 時,不過,這個 method 可傳回如 100644 這樣的字串,它是 getBits() 取得的數字以 8 進位表示成字串,後面三個數字是像 Unix 的權限表示法,644 就是 rw-r--r--。
差異比對不只可以比對不同 branch 間的差異,在文章一開頭顯示的那一張圖上的所有階段都可以比對。實際看過 API Documents 的人會發現,面的 FileMode 只記錄節點的基本屬性,那比我們想要了解的絕對少的多,要了解更多資訊,要從日誌查,下面的程式說明如何取得日誌 (git log)。
Repository repo = new FileRepositoryBuilder()
        .setGitDir(new File("D:/TestJGit/local/.git"))
        .build();
Git git = new Git(repo);
        
LogCommand logCmd = git.log();
ObjectId objId = repo.resolve("refs/remotes/origin/master");
Iterable<RevCommit> revCommit = logCmd.add(objId).call();
Iterator<RevCommit> commitIter = revCommit.iterator();
while (commitIter.hasNext()) {
    RevCommit commit = commitIter.next();
    System.out.println("commiter name = " + commit.getAuthorIdent().getName());
    System.out.println("commiter email = " + commit.getAuthorIdent().getEmailAddress());
    Date date = new Date(((long) commit.getCommitTime()) * 1000);
    System.out.println("commit time = " + date.toString());
}
透過 LogCommand 可以查得日誌,上面只簡單的舉幾個例子,像是 commit 的帳號、email、時間,詳細當然就請直接查 Documents。上面的時間有乘以 1000,是因為 getCommitTime() 傳回的值只到秒,而 Date 需要的是毫秒。



2015年10月22日 星期四

JGit - 基本命令

上一篇之後,這一篇說明一些 git 基本命令所對應的 API。在開始正式內容前,先說明一下我的環境,在我的電腦裡,有兩個目錄,分別為 D:/TestJGit/local 及 D:/TestJGit/remote,第一個目錄是 git repository 第二個是 bare repository,是第一個 repository 的遠端。
  • git init
File file = new File("D:/TestJGit/local");
Git git = Git.init().setDirectory(file).call();
git.close();
這個指令要注意,檔案系統中要已存在該目錄,才可以在該目錄建立 git repository。
  • git init --bare
Git git = Git.init().setDirectory(file).setBare(true).call();
setBare 預設是 false,所以第一個程式沒有呼叫這個 method,要建立 bare repository 只要如上呼叫 setBare 並傳入 true 即可。
  • git add
File file = new File("D:/TestJGit/local/.git");
Git git = Git.open(file);
AddCommand addCmd = git.add().addFilepattern("folder_1/local_d.txt");
addCmd.call();
git.close();
git 是分散式的版本控管,不管是要存取 local 或 remote 端,都先開啟 local 端的 git repository,要注意上面開始的是真正 git 的檔案所在的目錄 (.git),另一個要注意的是,要加入的檔案,是以 D:/TestJGit/local 目錄開始往下看,所以上面的檔案的完整路徑是 D:/TestJGit/local/folder_1/local_d.txt,不過,傳入時一定要如上傳相對路徑。
  • git commit
CommitCommand commitCmd = git.commit().setMessage("add a、b、d");
commitCmd.call();
  • git push
PushCommand pushCmd = git.push().setRemote("origin");
pushCmd.call();
只要牽涉到 remote 端,就有認證的問題,照理說要呼叫 setCredentialsProvider,這裡沒有呼叫這個 method,是因為我的 remote 端也在自己電腦的硬碟裡。(setCredentialsProvider 的用法請參考上一篇)
  • git clone
File file = new File("D:/TestJGit/remote");
Git git = Git.open(file);
CloneCommand cloneCmd = git.cloneRepository()
    .setBare(false)
    .setBranch("refs/remotes/origin/master")
    .setURI("D:/TestJGit/remote")
    .setDirectory(new File("D:/TestJGit/local3"));
cloneCmd.call();
git.close();
這裡要注意的是,開啟 remote 端的 bare repository,目錄名稱不是 .git 子目錄,事實上 bare repository 也沒有這個子目錄,這裡傳入 local repository 也可以。另外,setURI 是傳入 remote 端的路徑 (網址),setDirectory 則傳入 local repository 的路徑。
  • git pull
File file = new File("D:/TestJGit/local3/.git");
Git git = Git.open(file);
PullCommand pullCmd = git.pull();
pullCmd.setRemote("origin");
pullCmd.call();
git.close();
  • git rm
RmCommand rmCmd = git.rm().addFilepattern("local_a.txt");
rmCmd.call();
  • git diff
DiffCommand diffCmd = git.diff();
List<DiffEntry> diffEntry = diffCmd.call();
for(DiffEntry en:diffEntry) {
    System.out.println(en.toString());
}
這裡比對的是 working directory 和 最後一次 commit 間的差異,更深入的待下一篇說明。



2015年10月21日 星期三

concurrency programming

分類項目日期
1
Java
java.util.concurrent.locks - 臨界區間的讀寫
2015/08/13
2
Java
java.util.concurrent - Future & Callable
2015/08/15
3
Java
java.util.concurrent - ExecutorService
2015/08/16
4
C
fork Function
2015/06/06
5
Design Pattern
Producer-Consumer Pattern
2015/10/17



JGit - getting started

目前使用 Java 要操作 git,似乎只有 JGit 這組 API,從這篇開始,簡要的說明 JGit 的使用方法。在 JGit 中,最重要的兩個類別是 Repository 及 Git,Repository 類別可以用來存取 Git Repository (Git 容器) 中的屬性,Git 類別則提供了 Git 相關的指令。接下來舉一些實例:
  • 創建 Repository、Git
Repository repo = new FileRepositoryBuilder()
   .setGitDir(new File("D:/BitBucket/VersionControl/.git"))
   .build();
        
Git git = new Git(repo);
我的電腦中有一個目錄 D:/BitBucket/VersionControl,這是一個 git repository,在 BitBucket 這個網站上,有相對應的的一個 git bare repository,在創建 Repository 物件時,路徑指到本機的路徑,至於怎麼存取遠端的 bare repository 內的資料稍後說明。不管是 Repository 類別或 Git 類別,使用完都要呼叫 close() method,以關閉 I/O 連線。
  • 取得 remote name
如上圖所示,我的 git repository 設定了兩個 remote repository,要如何取得名稱呢? 如下:
Set<String> remoteNames = repo.getRemoteNames();




  •  取得本機 branch
除了預設的 master 外,我又建立了一個 TEST branch,以下是取得 branch 參考物件的方法。
ListBranchCommand branchCmd = git.branchList();
List<Ref> branch = branchCmd.call();
使用 Git 類別時,只要想想 git 有提供那些指令,大至上就可以找到相對的 method,上面的程式會取得 branch 的參考物件 (Ref),如果用 toString() 把它輸出,會如下:
Ref[refs/heads/TEST=e5dc4bc8a01cca3c74a3554f87857d468815b58b]
Ref[refs/heads/master=977a0b74fe3e49c166119d473f2415663e158260]
輸出的格式為 Ref[name=objectId],這表示如果我們要操作 branch,就可以透過這個方式取得 name 或 objectId 帶入其它 method 來操作 branch。
  • 取得遠端 branch
CredentialsProvider cp = new UsernamePasswordCredentialsProvider("username", "password");
Collection<Ref> lsRemote = git.lsRemote()
    .setCredentialsProvider(cp)
    .call();
        
for(Ref remote:lsRemote) {
    String objName = remote.getName();
            
    Ref localRef = repo.getRef(objName);
    System.out.println("local ref = " + localRef.toString());
            
    Ref remoteRef = remote.getTarget();
    System.out.println("remote ref = " + remoteRef.toString());
}
要取得遠端的物件,當然要認證,這裡採用帳號、密碼登入的方式,輸出會如下:
local ref = SymbolicRef[HEAD -> refs/heads/TEST=e5dc4bc8a01cca3c74a3554f87857d468815b58b]
remote ref = Ref[HEAD=6c248c9ae67bdf1fc65e3d5cc8110a5c017aba53]
local ref = Ref[refs/heads/master=977a0b74fe3e49c166119d473f2415663e158260]
remote ref = Ref[refs/heads/master=6c248c9ae67bdf1fc65e3d5cc8110a5c017aba53]
local ref = Ref[refs/heads/TEST=e5dc4bc8a01cca3c74a3554f87857d468815b58b]
remote ref = Ref[refs/heads/TEST=b6c238f6a641b4272dc0728a9db0ddd93690ce64]
可以看到,local 和 remote 的 object id 不同,對照直接下 git ls-remote 的輸出,就可以發現,上面的 remote ref 與 ls-remote 的輸出是一樣的。