diff --git a/.obsidian/daily-notes.json b/.obsidian/daily-notes.json index a92370e..cc8934b 100644 --- a/.obsidian/daily-notes.json +++ b/.obsidian/daily-notes.json @@ -1 +1,5 @@ -{"folder":"Daily","format":"YYYY-MM-DD(ddd)","template":"PARA/003 - Resources(資源)/99. templates/日記"} \ No newline at end of file +{ + "folder": "01. Daily", + "format": "YYYY-MM-DD(ddd)", + "template": "02. PARA/03. Resources(資源)/99. templates/日記" +} \ No newline at end of file diff --git a/.obsidian/workspace b/.obsidian/workspace index 5283b94..4a7b7c9 100644 --- a/.obsidian/workspace +++ b/.obsidian/workspace @@ -11,7 +11,7 @@ "type": "markdown", "state": { "file": "00. TOP/01. TODO.md", - "mode": "source", + "mode": "preview", "source": true } } @@ -35,25 +35,31 @@ "type": "mobile-drawer", "children": [ { - "id": "88e1cff7a30f6953", - "type": "leaf", - "state": { - "type": "file-explorer", - "state": {} - } - }, - { - "id": "ceb171daf1327c40", - "type": "leaf", - "state": { - "type": "search", - "state": { - "query": "", - "matchingCase": false, - "explainSearch": false, - "collapseAll": false, - "extraContext": false, - "sortOrder": "alphabetical" + "id": "dbd6f1d450f54a18", + "type": "tabs", + "children": [ + { + "id": "f0c230180a9faaa1", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": {} + } + }, + { + "id": "81a47da514b9d823", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "shared_ptr", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + } + } } } }, @@ -76,19 +82,50 @@ "type": "mobile-drawer", "children": [ { - "id": "e3c9f68a35edf017", - "type": "leaf", - "state": { - "type": "backlink", - "state": { - "file": "00. TOP/01. TODO.md", - "collapseAll": false, - "extraContext": false, - "sortOrder": "alphabetical", - "showSearch": false, - "searchQuery": "", - "backlinkCollapsed": false, - "unlinkedCollapsed": true + "id": "74ab9d565ea062c4", + "type": "tabs", + "children": [ + { + "id": "bcf01574fb3a7a3b", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "file": "00. TOP/01. TODO.md", + "collapseAll": true, + "extraContext": true, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": false + } + } + }, + { + "id": "509990f938113653", + "type": "leaf", + "state": { + "type": "advanced-tables-toolbar", + "state": {} + } + } + ] + }, + { + "id": "2b8688e445848e2a", + "type": "tabs", + "children": [ + { + "id": "11f4d517d7b320b2", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + } + } } } } @@ -98,14 +135,14 @@ "active": "94f0e8a81af6c9e2", "lastOpenFiles": [ "00. TOP/01. TODO.md", - "00. TOP/00. Inbox.md", - "02. PARA/03. Resources(資源)/C++17/Class template.md", - "02. PARA/03. Resources(資源)/C++17/C++17.md", - "02. PARA/03. Resources(資源)/01. 架站/04. Gitea.md", - "02. PARA/03. Resources(資源)/Tool Setup/Obisidian.md", - "README.md", - "test.md", - "02. PARA/03. Resources(資源)/Proxmox VE.md", - "02. PARA/03. Resources(資源)/01. 架站/01. Nginx Layer4 Reverse Proxy.md" + "02. PARA/03. Resources(資源)/C++17/rvalue.md", + "02. PARA/03. Resources(資源)/C++17/move operator.md", + "02. PARA/03. Resources(資源)/C++17/智慧指標.md", + "02. PARA/03. Resources(資源)/git/submodule.md", + "02. PARA/03. Resources(資源)/git.md", + "02. PARA/01. Project(專案)/008. Sentinel.md", + "02. PARA/03. Resources(資源)/C++17/lvalue.md", + "02. PARA/03. Resources(資源)/FFMPEG/01. Setup.md", + "02. PARA/03. Resources(資源)/FFMPEG/00. Introduction.md" ] } \ No newline at end of file diff --git a/00. TOP/01. TODO.md b/00. TOP/01. TODO.md index b7cb98c..cafbe3e 100644 --- a/00. TOP/01. TODO.md +++ b/00. TOP/01. TODO.md @@ -1,5 +1,5 @@ # ME -- [ ] 作筆記:C++17 - share_ptr +- [x] 作筆記:C++17 - share_ptr ✅ 2022-06-06 - [ ] 作筆記:C++17 - R value reference與std::move - [ ] 作筆記:C++17 - Lambda - [ ] 補充英格蘭與蘇格蘭的歷史 @@ -8,4 +8,30 @@ # WORK ## Logitech ### RobotRunQA -- [ ] 檢查Amily的test task 🛫 2022-05-30 📅 2022-06-03 \ No newline at end of file +- [x] 檢查Amily的test task 🛫 2022-05-30 📅 2022-06-03 ✅ 2022-06-06 +- 改進RobotRunQA report + - TT + - [x] 在測試項目的主欄位上顯示resolution, format, fps 🛫 2022-06-06 📅 2022-06-10 ✅ 2022-06-09 + - [ ] 在測試項目的主欄位上顯示test time 🛫 2022-06-06 📅 2022-06-10 + - [x] test config 🛫 2022-06-06 📅 2022-06-10 ✅ 2022-06-09 + - TestAllFormatsVTF + - [x] 在測試細項表格上顯示詳細說明 🛫 2022-06-06 📅 2022-06-10 ✅ 2022-06-07 + - [x] Show "VideoFormats: "MaxFpsMaxResolutionOnly" to outside, let user understand the test purpose. 🛫 2022-06-06 📅 2022-06-10 ✅ 2022-06-08 + - [x] List max fps of all resolution of all format on side bar of section 🛫 2022-06-06 📅 2022-06-10 ✅ 2022-06-08 + - TestGpsDiff + - [ ] List all format 🛫 2022-06-06 📅 2022-06-10 + +# All TODOs +```tasks +not done +path does not include 2021 +path does not include 2022/01 +path does not include 2022/02 +path does not include 2022/03 +path does not include 2022/04 +path does not include 02. PARA/04. Archives(歸檔) +path does not include 001. Kong +group by folder +group by filename +group by heading +``` \ No newline at end of file diff --git a/01. Daily/2022-06-06(週一).md b/01. Daily/2022-06-06(週一).md new file mode 100644 index 0000000..53d28f8 --- /dev/null +++ b/01. Daily/2022-06-06(週一).md @@ -0,0 +1,48 @@ +時間:10:21:22 + +### TAG + + +### All TODOs +```tasks +not done +path does not include 2021 +path does not include 2022/01 +path does not include 2022/02 +path does not include 2022/03 +path does not include 2022/04 +path does not include 02. PARA/04. Archives(歸檔) +path does not include 001. Kong +group by folder +group by filename +group by heading +``` + +--- + +### Doing TODOs +```tasks +not done +has start date +happens before tomorrow +path does not include 2021 +path does not include 2022/01 +path does not include 2022/02 +path does not include 2022/03 +path does not include 2022/04 +path does not include 02. PARA/04. Archives(歸檔) +path does not include 001. Kong +group by folder +group by filename +group by heading +``` + +--- + +### 新增TODO +#### 私事 + +#### 公事 + +### 今日回顧 +趁著早上還算是不太厭世,趕快來清一下TODO,先把簡單的shared_ptr寫一寫。 \ No newline at end of file diff --git a/02. PARA/03. Resources(資源)/99. templates/日記.md b/02. PARA/03. Resources(資源)/99. templates/日記.md index 8665d23..8a6d978 100644 --- a/02. PARA/03. Resources(資源)/99. templates/日記.md +++ b/02. PARA/03. Resources(資源)/99. templates/日記.md @@ -11,7 +11,7 @@ path does not include 2022/01 path does not include 2022/02 path does not include 2022/03 path does not include 2022/04 -path does not include 004 - Archives(歸檔) +path does not include 02. PARA/04. Archives(歸檔) path does not include 001. Kong group by folder group by filename @@ -30,7 +30,7 @@ path does not include 2022/01 path does not include 2022/02 path does not include 2022/03 path does not include 2022/04 -path does not include 004 - Archives(歸檔) +path does not include 02. PARA/04. Archives(歸檔) path does not include 001. Kong group by folder group by filename diff --git a/02. PARA/03. Resources(資源)/C++17/lvalue.md b/02. PARA/03. Resources(資源)/C++17/lvalue.md index da4b4b1..6c571ec 100644 --- a/02. PARA/03. Resources(資源)/C++17/lvalue.md +++ b/02. PARA/03. Resources(資源)/C++17/lvalue.md @@ -1,2 +1,3 @@ +lvalue 是指: - 等號左邊的值 -- 「有固定address」的變數 \ No newline at end of file +- 可以被「取址」的變數 \ No newline at end of file diff --git a/02. PARA/03. Resources(資源)/C++17/rvalue.md b/02. PARA/03. Resources(資源)/C++17/rvalue.md index f45517a..986fdb3 100644 --- a/02. PARA/03. Resources(資源)/C++17/rvalue.md +++ b/02. PARA/03. Resources(資源)/C++17/rvalue.md @@ -1,9 +1,210 @@ +rvalue 是指: - 等號右邊的值 - 臨時的值,例如運算的結果 +- 無法被取址(address-of)的物件 -# rvalue參考 -可以用兩個`&`來參考rvalue。例如: +## rvalue reference +一般的參考只能參考[[lvalue]],如下的程式是ok的: +```cpp +int a = 10; +int& b = a; ``` -int c{5}; -int&& rtepm {c + 3}; -``` \ No newline at end of file + +但是像這樣就不行了: +```cpp +int a = 10; +int b = 5; +int& c = a + b; +``` + +因為`a+b`是一個 rvalue(臨時的值,沒辦法取址),所以無法參考。 +但是可以用`&&`來參考 rvalue。例如: +```cpp +int a = 10; +int b = 5; +int&& c = a + b; // c = 15 +``` + +而不用這樣: +```cpp +int a = 10; +int b = 5; +int r = a + b; +int& c = r; +``` + +了解 rvalue reference 之後,就可以實作類別的 move constructor 跟 move assignment operator。這可以減少複製的成本。 + +## Move constructor +假設我們有一個 class 叫 BigBuffer,定義如下: +```cpp +class BigBuffer { +public: + BigBuffer(int size=100*1024*1024) : + bufferSize(size) + { + std::cout << "BigBuffer constructor\n"; + this->buffer = std::make_unique(bufferSize); + } + + ~BigBuffer() { + std::cout << "BigBuffer destructor\n"; + } + + BigBuffer(const BigBuffer& src) { + std::cout << "BigBuffer copy constructor\n"; + bufferSize = src.bufferSize; + buffer = std::make_unique(bufferSize); + std::memcpy(buffer.get(), src.buffer.get(), bufferSize); + } + + BigBuffer& operator= (BigBuffer& src) { + std::cout << "BigBuffer copy operator\n"; + bufferSize = src.bufferSize; + buffer = std::make_unique(bufferSize); + std::memcpy(buffer.get(), src.buffer.get(), bufferSize); + return *this; + } + +private: + int bufferSize = 0; + std::unique_ptr buffer = nullptr; +}; +``` + +這個 class 的特色就是每一次使用都會佔用100MB的記憶體空間,想像下面的程式的動作: +```cpp +BigBuffer buf1; +// Do something with buf1 +// Assign to buf2 +BigBuffer buf2 = buf1; +``` + +執行訊息: +``` +BigBuffer constructor // create buf1 +BigBuffer copy constructor, copy 104857600Bytes // copy buf1 to buf2 +... +``` + +這會先產生 buf1,然後把 buf1 copy 給 buf2。如果我們想要省下 copy 的成本,這時候 Move constructor 就可以派上用場了。 +幫 BigBuffer 加一個 Move constructor: +```cpp +class BigBuffer { +public: + ... + + BigBuffer(BigBuffer&& src) noexcept { + std::cout << "BigBuffer move constructor\n"; + bufferSize = src.bufferSize; + buffer = std::move(src.buffer); + + src.buffer.reset(); + src.bufferSize = 0; + } + ... +}; +``` + +這個 move constructor 的參數就是一個 rvalue reference,我們把來源的 bufferSize 跟 buffer 指標「移到」我們這邊,而不是完整的複製一份。在轉移之後呢,當然也要把來源清空,讓轉移更加明確。 + +有了 Move assignment operator 之後,在執行一次原本的程式,你會發現訊息......沒有變,還是一樣呼叫 copy constructor 來複製了100MB 的 buffer,這時我們需要明確的告訴 compiler 我們要「移動」物件,而不是複製它,把原本的程式改為: +```cpp +BigBuffer buf1; +// Do something with buf1 +// Assign to buf2 +BigBuffer buf2 = std::move(buf1); +``` + +我們用 `std::move()` 來「移動」物件,這時輸出變成 +``` +BigBuffer constructor // create buf1 +BigBuffer move constructor // move buf1 to buf2, buf1 has nullptr now +... +``` + +另外一個情形也可以受益於此,假如我們有個 function 會產生 `BigBuffer`,如下: +```cpp +BigBuffer BigBufferCreator() { + std::cout << "BigBufferCreator: Create a BigBuffer!\n"; + BigBuffer tempb; + // do something + std::cout << "BigBufferCreator: return\n"; + return tempb; +} + +BigBuffer b = BigBufferCreator(); // copy tempb to b +``` + +在沒有 Move constructor 的情況下,上面的程式會先產生一個 `tempb`,然後複製給 `b`,訊息: +``` +BigBufferCreator: Create a BigBuffer! +BigBuffer constructor +BigBufferCreator: return +BigBuffer copy constructor, copy 104857600Bytes // Copy 100MB! +... +``` + +在有 Move constructor 的情況下,訊息就變成: +``` +BigBufferCreator: Create a BigBuffer! +BigBuffer constructor +BigBufferCreator: return +BigBuffer move constructor // Use MOVE! +BigBuffer destructor +BigBuffer destructor +``` + +因為 `BigBufferCreator()` 產生的就是一個 `BigBuffer` rvalue,所以 compiler 會使用 move constructor(`BigBuffer(BigBuffer&& src)`) 而不是 copy constructor。 + +## Move assignment operator(`=`) +Move assignment operator 的行為跟 move constructor 是一樣的,幫 `BigBuffer` 加入 move assignment operator: +```cpp +class BigBuffer { +public: + ... + + BigBuffer& operator=(BigBuffer&& src) noexcept { + std::cout << "BigBuffer move operator\n"; + bufferSize = src.bufferSize; + buffer = std::move(src.buffer); + + src.buffer.reset(); + src.bufferSize = 0; + return *this; + } + ... +}; +``` + +測試程式: +```cpp +BigBuffer b1, b2; +b2 = b1; +``` + +訊息: +``` +BigBuffer constructor +BigBuffer constructor +BigBuffer copy operator, copy 104857600Bytes +``` + +還是使用 copy assignment operator 來複製,理由是一樣的,需要一個明確的 `std::move()` 來表示「轉移」的行動,把程式改成: +```cpp +BigBuffer b1, b2; +b2 = std::move(b1); +``` + +這樣就可以了。訊息: +``` +BigBuffer constructor +BigBuffer constructor +BigBuffer move operator // Use MOVE! +``` + +## 參考 +- [Value categories - cppreference.com](https://en.cppreference.com/w/cpp/language/value_category) +- [rvalue 參考](https://openhome.cc/Gossip/CppGossip/RvalueReference.html) +- [Move constructors - cppreference.com](https://en.cppreference.com/w/cpp/language/move_constructor) +- [Move assignment operator - cppreference.com](https://en.cppreference.com/w/cpp/language/move_assignment) diff --git a/02. PARA/03. Resources(資源)/C++17/智慧指標.md b/02. PARA/03. Resources(資源)/C++17/智慧指標.md new file mode 100644 index 0000000..d74fd00 --- /dev/null +++ b/02. PARA/03. Resources(資源)/C++17/智慧指標.md @@ -0,0 +1,110 @@ +[`unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr)與[`shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr)都是智慧指標,箱對於原本的raw pointer,智慧指標使用起來更方便,也不用擔心delete的問題。 + +## unique_ptr +`unique_ptr` 的特點是,它保證在一個時間內,只會有一個指標的擁有者,也就是這個指標不能被複製跟移動,當 `unique_ptr` 離開它的scope時候,它所擁有的指標也隨之被delete。這讓你不用擔心memory leak的問題。 +假設我們有一個class叫 `BigBuffer` ,原本分配記憶體的方法: +```cpp +BigBuffer* bigBuf = new BigBuffer(bufferSize); +// Use buffer here +delete bigBuf; +``` + +用 `unique_ptr`: +```cpp +auto bigBuf = std::make_unique(bufferSize); +// Use buffer here +// bigBuf will be released when exiting scope +``` + +我們統一用[`std::make_unique<>`](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique)這個template function來建立 `unique_ptr` ,角括號 `<>` 裡面要帶入你要建立的型別,後面的括號 `()` 就是型別的constructor,使用起來跟 `new` 是一樣的。 +因為 `std::make_unique<>` 裡面已經有表明型別了,所以變數就用 `auto` 就可以了,不用再寫一次型別。 + +一旦 `unique_ptr` 建立之後,使用起來就跟一般指標沒有兩樣,都是用 `->` 來操作:`bigBuf->setXXX()` or `bigBuf->getXXX()`。 +但是別忘記 `unique_ptr` 本身還是一個local variable,所以我們可以用 `.` 來操作 `unique_ptr` ,例如我們可以用 `.reset()` 重新配一個指標: +```cpp +BigBuffer* pBuffer = new BigBuffer(); +bigBuf.reset(pBuffer); +``` +這時候舊指標會自動delete,如果記憶體分配有成功的話,bigBuf會接管剛剛new出來的指標,或者變成 `nullptr` (記憶體分配失敗)。 + +如果單純想要釋放指標,那就單純的呼叫 `reset()` 就好。 +```cpp +bigBuf.reset(); // Now I'm nullptr +``` + +如果要分配陣列的話: +```cpp +auto intArray = std::make_unique(1024); +``` + +使用方式也是一樣的: +```cpp +intArray[5] = 555; +``` + +不過對於陣列的操作更建議使用 `std::array` 。 + +如果有什麼特殊原因讓你決定不再讓 `unique_ptr` 來幫你管理指標,可以用 `release()` 來讓出指標: +```cpp +auto intArray = std::make_unique(1024); +int* intArrayRaw = intArray.release(); // Now I don't care anymore +``` +但是這時候呼叫 `delete[]` (或 `delete` )的責任又回到你身上了。所以千萬不要把 `release()` 跟 `reset()` 搞混了。 + +`unique_ptr` 不能被複製跟移動,所以下列的寫法都編不過: +```cpp +auto ptr1 = std::make_unique(5); +std::unique_ptr ptr2(ptr1); // Error +std::unique_ptr ptr2 = ptr1; // Error +``` +在Visual Studio 2017上,錯誤訊息是這樣:`error C2280: 'std::unique_ptr>::unique_ptr(const std::unique_ptr> &)': attempting to reference a deleted function`。 +其實就是`unique_ptr`的copy constructor跟assignment operator都被標記為delete了。 + +### Move a unique_ptr +如果一定要把 unique_ptr 指定給別人可以嗎?可以的,用 `std::move()` 來轉移: +```cpp +auto ptr1 = std::make_unique(5); +// do something +auto anotherPtr = std::move(ptr1); +``` + +ptr1原本所管理的指標會轉移給 anotherPtr,ptr1 會變成 nullptr。 + +## shared_ptr +建立一個 `shared_ptr` 是使用[`std::make_shared()`](https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared): +```cpp +auto myBuf = std::make_shared(bufferSize); +``` + +但是 `shared_ptr` 可以被複製與移動,這是跟 `unique_ptr` 的差別: +```cpp +auto myBuf = std::make_shared(bufferSize); + +std::shared_ptr bufCopy = myBuf; +``` + +現在 bufCopy 跟 myBuf 都指向同一個指標,他們都可以操作這個指標: +```cpp +myBuf->setZero(startAddr, endAddr); +bufCopy->setOne(startAddr, endAddr); +``` + +`shared_ptr` 內部有一個參考記數(reference count)來紀錄它所擁有的指標已經分享給幾個變數了,只要有變數離開了他的scope,參考記數就會減少,反之,要是像上面那樣有人複製指標,參考記數就會增加,參考記數歸0的時候,指標就會被釋放。 + +有了 `shared_ptr` 我們就不必擔心 delete 的責任問題: +```cpp +std::shared_ptr getBuffer(int32_t bufferSize) { + return std::make_shared(bufferSize); +} + +int main() { + auto myBuf = getBuffer(1024); // new(malloc) memory + // use myBuf + + return 0; +} // myBuf delete memory here +``` + +`shared_ptr` 有一個問題是可以會「循環參考」(cyclic references),也就是 share_ptr A 指向另一個 share_ptr B ,然後 share_ptr B 又指向 share_ptr A,這造成參考記數(reference count)不會減少而永遠無法釋出指標。這個是需要注意的。 + +但是 `shared_ptr` 還是讓記憶體的管理問題大大減少,應該用 `shared_ptr` 來代替 `new` & `delete` 。 diff --git a/02. PARA/03. Resources(資源)/FFMPEG/00. Introduction.md b/02. PARA/03. Resources(資源)/FFMPEG/00. Introduction.md new file mode 100644 index 0000000..3512cc4 --- /dev/null +++ b/02. PARA/03. Resources(資源)/FFMPEG/00. Introduction.md @@ -0,0 +1,17 @@ +- [FFmpeg](https://www.ffmpeg.org/) +- Open source +- For handling multimedia files and streams +- GPL/LGPS license +- Inspired by MPEG(Moving Picture Experts Group), FF means "Fast Foward" +- Libraries + - libavcodec: Contain all the encoders and decoders + - libavformat: Muxers and demuxers + - libavfilter: Filters that you can use on your video and audio according to your requirements + - libavdevice: Input and output devices + - libavutil + - libswscale + - libswresample +- Tools + - ffmpeg: Main transcoding engine + - ffplay: Minimum tool to play a video or audio + - ffprobe: For inspecting medias and extract valuable information \ No newline at end of file diff --git a/02. PARA/03. Resources(資源)/FFMPEG/01. Setup.md b/02. PARA/03. Resources(資源)/FFMPEG/01. Setup.md new file mode 100644 index 0000000..2c636e4 --- /dev/null +++ b/02. PARA/03. Resources(資源)/FFMPEG/01. Setup.md @@ -0,0 +1,30 @@ +FFmpeg doesn't support any official builds for any operation systems. You can build the codes from source, or use the prebuilt packages. + +## From source +Get source code from [snapshot](https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2) or git(`git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg`). + +## From prebuilt packages +### MacOS +Use homebrew to install FFMPEG package +``` +brew install ffmpeg +``` + +### Ubuntu +``` +sudo apt update ;\ +sudo apt install ffmpeg +``` + +### Windows +Goto https://www.ffmpeg.org/download.html, and +![[Pasted image 20220607105807.png]] + +And then browser jumps to https://www.gyan.dev/ffmpeg/builds/, click [release build](https://www.gyan.dev/ffmpeg/builds/#release-builds), download **release-full**. +![[Pasted image 20220607110211.png]] + +Unzip the file, then put the folder to anywhere you like, e.g. `C:\libs`, so the full path of ffmpeg packages should looks like: `C:\libs\ffmpeg-5.0.1-full_build` + +#### Add to system variable +Add the path of bin of ffmpeg packages to `PATH` system variable. +![[Pasted image 20220607110949.png]] \ No newline at end of file diff --git a/02. PARA/03. Resources(資源)/git/submodule.md b/02. PARA/03. Resources(資源)/git/submodule.md new file mode 100644 index 0000000..b519b86 --- /dev/null +++ b/02. PARA/03. Resources(資源)/git/submodule.md @@ -0,0 +1,27 @@ +## 建立 Git Submodule +``` +git submodule add [] +``` +新增之後,用 git status 會發現多了兩個東西需要 commit: +![[Pasted image 20220608152709.png]] + +第一個檔案 .gitmodules,裡面紀錄 submodule 的對應關係,內容大概像這樣: +``` +[submodule "RobotRunQA"] + path = RobotRunQA + url = git@github.com-logi:JuiwenHsu/RobotRunQA.git +``` + +接下來剩3個步驟: +1. 提交 `.gitmodule`:`git add .gitmodule ; git push origin master` +2. 跟git說我們有新增一個submodule:`git submodule init` +3. 把submodule pull下來:`git submodule update` + +## Clone repository and submodule +當clone一個有submodule的repo的時候,我們還需要 `git submodule init` 跟 `git submodule update` ,例如: +``` +git clone https://xxx/xxx.git +cd xxx +git submodule init +git submodule update +``` \ No newline at end of file diff --git a/02. PARA/01. Project(專案)/004. Group firmware update check.md b/02. PARA/04. Archives(歸檔)/01. Project(專案)/004. Group firmware update check.md similarity index 100% rename from 02. PARA/01. Project(專案)/004. Group firmware update check.md rename to 02. PARA/04. Archives(歸檔)/01. Project(專案)/004. Group firmware update check.md diff --git a/attachments/Pasted image 20220607105807.png b/attachments/Pasted image 20220607105807.png new file mode 100644 index 0000000..1a05b69 Binary files /dev/null and b/attachments/Pasted image 20220607105807.png differ diff --git a/attachments/Pasted image 20220607110211.png b/attachments/Pasted image 20220607110211.png new file mode 100644 index 0000000..9c2a2d6 Binary files /dev/null and b/attachments/Pasted image 20220607110211.png differ diff --git a/attachments/Pasted image 20220607110949.png b/attachments/Pasted image 20220607110949.png new file mode 100644 index 0000000..9265c04 Binary files /dev/null and b/attachments/Pasted image 20220607110949.png differ diff --git a/attachments/Pasted image 20220608152709.png b/attachments/Pasted image 20220608152709.png new file mode 100644 index 0000000..3b407b4 Binary files /dev/null and b/attachments/Pasted image 20220608152709.png differ