Git repository 一分為二的做法
活著 (active) 的專案隨著時間過去只會愈長愈大,豬養肥了就是要殺? 不管理由如何,總是有重整的機會。一般的情況下,目錄結構都是有組織的,也就是同性質的 resource 會擺放在一起,所以大部份的 case 都是拆單一目錄出來為獨立的 git repository。
依目錄拆 repository,換句話說就是把該目錄的 commit 全部保留,其餘的不要,當你建出這一個分支時,剩下的就只是合併剩下的成果而已,只要掌握這個概念的話,下面的操作就不難理解。
一開始我的土炮做法,以 laravel/framework 1 為例子,把 illuminate/database
拆出來。
首先做不做備份取決於你的信心,因為 remote 那有一份了,所以我自已都沒有做。
你可以另外 clone 一份 repository 下來,也可以另外開 branch 出來操作。
$ git clone git@github.com:laravel/framework.git
開一個 database branch 來操作
$ cd framework
framework (master) $ git checkout -b database
framework (database) $
利用 git filter-branch
來改寫歷史
framework (database) $ git filter-branch --subdirectory-filter src/Illuminate/Database/ database
Rewrite adbb29172a4a70972c15e9564649dd4b520fcf66 (1059/1059)
Ref 'refs/heads/database' was rewritten
結束後可以看到 src/Illuminate/Database 目錄下的檔案都攤在根目錄了
framework (database) $ ls -l
total 136
drwxrwxr-x 10 gasolwu gasolwu 4096 6月 17 01:35 ./
drwxrwxr-x 10 gasolwu gasolwu 4096 6月 17 01:28 ../
drwxrwxr-x 2 gasolwu gasolwu 4096 6月 17 01:35 Capsule/
-rwxrwxr-x 1 gasolwu gasolwu 889 6月 17 01:35 composer.json*
-rwxrwxr-x 1 gasolwu gasolwu 2895 6月 17 01:35 ConnectionInterface.php*
-rwxrwxr-x 1 gasolwu gasolwu 20643 6月 17 01:35 Connection.php*
-rwxrwxr-x 1 gasolwu gasolwu 502 6月 17 01:35 ConnectionResolverInterface.php*
-rwxrwxr-x 1 gasolwu gasolwu 1615 6月 17 01:35 ConnectionResolver.php*
drwxrwxr-x 2 gasolwu gasolwu 4096 6月 17 01:35 Connectors/
drwxrwxr-x 3 gasolwu gasolwu 4096 6月 17 01:35 Console/
-rwxrwxr-x 1 gasolwu gasolwu 6141 6月 17 01:35 DatabaseManager.php*
-rwxrwxr-x 1 gasolwu gasolwu 1267 6月 17 01:35 DatabaseServiceProvider.php*
drwxrwxr-x 3 gasolwu gasolwu 4096 6月 17 01:35 Eloquent/
drwxrwxr-x 8 gasolwu gasolwu 4096 6月 17 01:35 .git/
-rwxrwxr-x 1 gasolwu gasolwu 3611 6月 17 01:35 Grammar.php*
drwxrwxr-x 3 gasolwu gasolwu 4096 6月 17 01:35 Migrations/
-rwxrwxr-x 1 gasolwu gasolwu 5537 6月 17 01:35 MigrationServiceProvider.php*
-rwxrwxr-x 1 gasolwu gasolwu 1444 6月 17 01:35 MySqlConnection.php*
-rwxrwxr-x 1 gasolwu gasolwu 1194 6月 17 01:35 PostgresConnection.php*
drwxrwxr-x 4 gasolwu gasolwu 4096 6月 17 01:35 Query/
-rw-rw-r-- 1 gasolwu gasolwu 1340 6月 17 01:35 QueryException.php
-rwxrwxr-x 1 gasolwu gasolwu 2198 6月 17 01:35 README.md*
drwxrwxr-x 3 gasolwu gasolwu 4096 6月 17 01:35 Schema/
-rwxrwxr-x 1 gasolwu gasolwu 1661 6月 17 01:35 Seeder.php*
-rwxrwxr-x 1 gasolwu gasolwu 975 6月 17 01:35 SeedServiceProvider.php*
-rwxrwxr-x 1 gasolwu gasolwu 1133 6月 17 01:35 SQLiteConnection.php*
-rwxrwxr-x 1 gasolwu gasolwu 2122 6月 17 01:35 SqlServerConnection.php*
這時後也可以用 tig
或 git log
看一下 history 長什麼樣子。
framework (database) $ git log -m -stat -n 1
commit 291c2e9139086aefefb3db32e56675c67d92c1ec (from ec71e594c245eeb5c05d6de7a770399e045341a4)
Merge: ec71e59 74b8089
Author: Taylor Otwell <taylorotwell@gmail.com>
Date: Fri Jun 13 15:12:36 2014 -0500
Merge branch '4.2'
Console/Migrations/RefreshCommand.php | 10 ++++++++--
Schema/Blueprint.php | 10 ++++++++++
2 files changed, 18 insertions(+), 2 deletions(-)
commit 291c2e9139086aefefb3db32e56675c67d92c1ec (from 74b8089b6ff5aed2129cbd7e7559144b68d3f660)
Merge: ec71e59 74b8089
Author: Taylor Otwell <taylorotwell@gmail.com>
Date: Fri Jun 13 15:12:36 2014 -0500
Merge branch '4.2'
Query/Builder.php | 46 ++++++++++++++++++++++++++++++++++++++++++++--
composer.json | 23 +++++++++++------------
2 files changed, 55 insertions(+), 14 deletions(-)
commit 74b8089b6ff5aed2129cbd7e7559144b68d3f660
Author: Taylor Otwell <taylorotwell@gmail.com>
Date: Fri Jun 13 14:28:23 2014 -0500
Working on documentation block.
Schema/Blueprint.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
如果不想攤在根目錄,而是放在 src 目錄的話,要再多做一步 filter-branch
,--tree-filter 後面接的是 command 會被 shell 執行 (透過 eval
),所以這邊我們建立 src/ 目錄 (replay 當下可能不存在),然後判斷根目錄的每個檔案如果不是一開始建立的 src/ 目錄,就把它搬到 src/ 裡去
framework (database) $ git filter-branch -f --tree-filter 'mkdir -p src; for d in *; do if [ $d != "src" ]; then mv $d src/; fi; done;' database
Rewrite 291c2e9139086aefefb3db32e56675c67d92c1ec (1059/1059)
Ref 'refs/heads/database' was rewritten
結果就變這樣
framework (database) $ tree -d
.
└── src
├── Capsule
├── Connectors
├── Console
│ └── Migrations
├── Eloquent
│ └── Relations
├── Migrations
│ └── stubs
├── Query
│ ├── Grammars
│ └── Processors
└── Schema
└── Grammars
14 directories
最後,因為歷史被改寫的關係,master
與 database
branch 除了 commit 訊息相似之外,已經是兩條平行的 tree 了,只要簡單 init 一個 git repository,並把創造出來的 database
branch fetch 回來,然後 merge 就結束了 (等同於 git pull 的操作)。
$ cd /path/to/new_repo
new_repo $ git init
Initialized empty Git repository in /path/to/new_repo/.git/
new_repo (master) $ git fetch /path/to/framework database
remote: Counting objects: 5889, done.
remote: Compressing objects: 100% (1821/1821), done.
remote: Total 5889 (delta 3026), reused 3762 (delta 3007)
Receiving objects: 100% (5889/5889), 1.54 MiB | 0 bytes/s, done.
Resolving deltas: 100% (3026/3026), done.
From ../framework
* branch database -> FETCH_HEAD
new_repo (master) $ git merge FETCH_HEAD
想像一下,補上 composer.json
和 autoload PSR-4 後,是不是就像是一個獨立的 composer package,建議可以用這個方式拆 module,module 一多就可以利用 composer 的 replace 來組 full stack 的 package,有點像是 Debian dummy package 或 FreeBSD meta port 的概念,拆細的彈性就在這裡。
另外補充一下其他作法,--subdirectory-filter 那邊可以用 git subtree
,上面兩段 filter-branch
可以用下面一行來完成...
framework (database) $ git filter-branch --subdirectory-filter src/Illuminate/Database/ --index-filter 'git ls-files -s | sed "s|\t\"*|&src/|" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' database
指令有點長,不過這一段有寫在 manpage 的範例裡,所以不用刻意去背它。