小緯
  • NEW!
    NEW!  Connect Ideas Across Notes
    Save time and share insights. With Paragraph Citation, you can quote others’ work with source info built in. If someone cites your note, you’ll see a card showing where it’s used—bringing notes closer together.
    Got it
      • Create new note
      • Create a note from template
        • Sharing URL Link copied
        • /edit
        • View mode
          • Edit mode
          • View mode
          • Book mode
          • Slide mode
          Edit mode View mode Book mode Slide mode
        • Customize slides
        • Note Permission
        • Read
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Write
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invite by email
        Invitee

        This note has no invitees

      • Publish Note

        Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

        Your note will be visible on your profile and discoverable by anyone.
        Your note is now live.
        This note is visible on your profile and discoverable online.
        Everyone on the web can find and read all notes of this public team.

        Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Explore these features while you wait
        Complete general settings
        Bookmark and like published notes
        Write a few more notes
        Complete general settings
        Write a few more notes
        See published notes
        Unpublish note
        Please check the box to agree to the Community Guidelines.
        View profile
      • Commenting
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
        • Everyone
      • Suggest edit
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
      • Emoji Reply
      • Enable
      • Versions and GitHub Sync
      • Note settings
      • Note Insights New
      • Engagement control
      • Make a copy
      • Transfer ownership
      • Delete this note
      • Save as template
      • Insert from template
      • Import from
        • Dropbox
        • Google Drive
        • Gist
        • Clipboard
      • Export to
        • Dropbox
        • Google Drive
        • Gist
      • Download
        • Markdown
        • HTML
        • Raw HTML
    Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
    Create Create new note Create a note from template
    Menu
    Options
    Engagement control Make a copy Transfer ownership Delete this note
    Import from
    Dropbox Google Drive Gist Clipboard
    Export to
    Dropbox Google Drive Gist
    Download
    Markdown HTML Raw HTML
    Back
    Sharing URL Link copied
    /edit
    View mode
    • Edit mode
    • View mode
    • Book mode
    • Slide mode
    Edit mode View mode Book mode Slide mode
    Customize slides
    Note Permission
    Read
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Write
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    ## Yocto Project (3):在 Yocto 上建立 OpenBMC 前情提要-[安裝 Yocto Project 在 Ubuntu 24.04 上並建立 Poky 系統](https://hackmd.io/@zhonwei/ryqxAHXnWx) 現在的學習路徑是要跟著 [OpenBMC Developer Documentation](https://github.com/openbmc/docs/blob/master/development/README.md) 學習 OpenBMC 的開發 1. Development Environment Setup Start here. This shows how to setup an OpenBMC development environment using its bitbake build process and how to start the software emulator, QEMU. 2. Hello World This shows how to use the yocto tool, devtool, to extract an OpenBMC source repository, build, and test your changes within QEMU. 3. Web UI Development This shows how to modify the phosphor-webui web application and test your changes within QEMU. 4. Code Reviews Using Gerrit This shows how to setup your environment to utilize Gerrit for submitting your changes for code review. --- ## Building OpenBMC 現在依照 [OpenBMC Development Environment](https://github.com/openbmc/openbmc/blob/master/README.md#1-prerequisite) 上面的步驟,建立一個針對 Romulus 主機板設置的 image。 更新套件 ```b $ sudo apt-get install build-essential chrpath cpio debianutils diffstat file gawk gcc git iputils-ping libacl1 locales python3 python3-git python3-jinja2 python3-pexpect python3-pip python3-subunit socat texinfo unzip wget xz-utils zstd ``` 如果 Linux 系統是 Ubuntu 24.04 的話還需要先輸入這個指令 ```bash echo 'kernel.apparmor_restrict_unprivileged_userns = 0' | \ sudo tee /etc/sysctl.d/99-bitbake.conf sudo sysctl -p /etc/sysctl.d/99-bitbake.conf ``` Clone OpenBMC 的儲存庫(Clone OpenBMC Repository) ```bash $ git clone https://github.com/openbmc/openbmc.git $ cd openbmc ``` 針對你的硬體進行設定(Target your hardware) ```bash $ . setup romulus ``` 開始使用 bitbake 編譯 ```bash $ bitbake obmc-phosphor-image ``` 這樣就生成了一個專為 romulus 主機板設定的 image 檔。它的路徑是 `~/openbmc/build/romulus/tmp/deploy/images/romulus`,名稱叫做 `obmc-phosphor-image-romulus.static.mtd` --- ## Download and Start QEMU Session 1. 下載最新的 openbmc/qemu fork ```bash $ wget https://jenkins.openbmc.org/job/latest-qemu-x86/lastSuccessfulBuild/artifact/qemu/build/qemu-system-arm $ chmod u+x qemu-system-arm ``` :::info QEMU 在這裡的角色是硬體模擬器,讓您不需要實際的實體伺服器就能執行 OpenBMC 韌體。 ::: :::info Fork 指的是從原始專案的分支或衍生版本 ::: 2. 複製生成好的 image 到跟 qemu-system-arm 同一層(在 /build/romulus 底下) ```bash $ cp ~/openbmc/build/romulus/tmp/deploy/images/romulus/obmc-phosphor-image-romulus.static.mtd ./ ``` 3. Romulus image 開始進行 QEMU ```bash $ ./qemu-system-arm -m 256 -machine romulus-bmc -nographic \ -drive file=./obmc-phosphor-image-romulus.static.mtd,format=raw,if=mtd \ -net nic \ -net user,hostfwd=:127.0.0.1:2222-:22,hostfwd=:127.0.0.1:2443-:443,hostfwd=tcp:127.0.0.1:2080-:80,hostfwd=tcp:127.0.0.1:2200-:2200,hostfwd=udp:127.0.0.1:2623-:623,hostfwd=udp:127.0.0.1:2664-:664,hostname=qemu ``` 4. 等待 QEMU 模擬的 BMC 開機,登入使用預設的使用者 root 和密碼 0penBmc (0 是數字零) ```bash romulus login: root Password: ``` 成功之後就會看到 ```bash root@romulus:~# ``` 5. 檢察系統的狀態 使用 obmcutil tool 來確認 OpenBMC 的服務狀態 ```bash root@romulus:~# obmcutil state CurrentBMCState : xyz.openbmc_project.State.BMC.BMCState.NotReady CurrentPowerState : xyz.openbmc_project.State.Chassis.PowerState.Off CurrentHostState : xyz.openbmc_project.State.Host.HostState.Off BootProgress : xyz.openbmc_project.State.Boot.Progress.ProgressStages.Unspecified OperatingSystemState: xyz.openbmc_project.State.OperatingSystem.Status.OSStatus.Inactive ``` :::info BMCState 是 BMC 本身的狀態,也就是管理控制器(現在跑的這個系統)有沒有準備好對外提供服務,例如 REST API、SSH、IPMI 這些介面是否可用。Ready 才表示可以正常管理。 ::: :::info PowerState 是 Chassis(機箱)的電源狀態,代表整台伺服器的物理電源有沒有接通。Off 就是機箱電源是關的,On 表示機箱有通電。這對應到真實伺服器上就是電源供應器有沒有供電給主機板。 ::: :::info HostState 是 Host 的狀態,也就是跑在伺服器上的主作業系統(例如 Linux 或 Windows)有沒有在運作。即使機箱通電了,Host 也可能還在開機中或是關機狀態。 ::: CurrentBMCState 這欄是 NotReady,有可能是剛開機要給它啟動的時間,但如果等了一陣子都是 Not Ready,就需要來 debug 一下。 :::warning systemctl list-jobs --no-pager 可以用來確認哪個 service 執行完畢、哪個 service 卡住 ::: ```bash root@romulus:~# systemctl list-jobs --no-pager JOB UNIT TYPE STATE ------------------------------------------------------------------------ 179 phosphor-discover-system-state@0.service start waiting 173 obmc-led-group-start@bmc_booted.service start waiting 1 multi-user.target start waiting 94 obmc-fru-fault-monitor.service start waiting 250821 xyz.openbmc_project.LED.GroupManager.service start running 95 mapper-wait@-xyz-openbmc_project-led-groups.service start running ``` running 代表正在執行 :::warning 輸入 journalctl --no-pager 可以確認 LED.GroupManager.service 發生什麼事情 ::: ```bash root@romulus:~# journalctl -u xyz.openbmc_project.LED.GroupManager.service -n 50 --no-pager Apr 20 06:31:10 romulus systemd[1]: xyz.openbmc_project.LED.GroupManager.service: Failed with result 'timeout'. Apr 20 06:31:10 romulus systemd[1]: Failed to start Phosphor LED Group Management Daemon. Apr 20 06:31:11 romulus systemd[1]: xyz.openbmc_project.LED.GroupManager.service: Scheduled restart job, restart counter is at 3700. ``` LED GroupManager 重啟了 3700 次,每次都是啟動 90 秒後 timeout,代表它沒辦法成功初始化,這是因為 phosphor-ledmanager 啟動時會去讀取 LED 的硬體設定檔(JSON config),這個設定檔描述了這塊板子上有哪些 LED。但在 QEMU 模擬環境下,它找不到或讀不到這個設定檔,導致程式無法初始化,也就無法在 D-Bus 上完成 xyz.openbmc_project.LED.GroupManager 的註冊。 解決方法:參考 https://github.com/openbmc/phosphor-led-manager/issues/14 給 phosphor-ledmanager 一個空的 JSON config {},讓它認為「這塊板子上沒有任何 LED」,程式就能正常初始化並在 D-Bus 上完成註冊,systemd 也就認為它啟動成功了 首先,建立一個空的 json 檔 ```bash root@romulus:~# echo '{}' > /tmp/led-empty.json ``` :::info echo '{}':在終端機印出 {}(代表一個空的 JSON 物件)。 `>`:是重新導向符號,會將左邊輸出的內容寫入右邊的檔案。如果檔案已存在,內容會被覆蓋;如果不存在,則會建立新檔。 /tmp/led-empty.json:目標檔案的路徑。它在 /tmp 目錄(暫存資料夾)下建立一個叫做 led-empty.json 的檔案。 ::: 在背景啟動 OpenBMC 的 LED 管理服務,並強制它使用剛才建立的那個空設定檔。 ```bash root@romulus:~# /usr/libexec/phosphor-led-manager/phosphor-ledmanager -c /tmp/led-empty.json & ``` :::info /usr/libexec/phosphor-led-manager/phosphor-ledmanager:這是程式的完整路徑,執行 OpenBMC 中負責控制 LED 燈光(如告警燈、定位燈)的主程式。 -c /tmp/led-empty.json:-c 代表 config。這是在告訴程式:「不要用預設的設定,請讀取 /tmp/led-empty.json 這個檔案當作你的設定檔」。 &:它代表在背景執行(Background)。這樣指令送出後,程式會持續執行,但終端機不會被佔據,可以繼續輸入其他指令。 ::: 樹狀結構列出 LED 管理服務在 D-Bus 系統匯流排上註冊的所有物件路徑.確認有沒有成功註冊到 D-Bus ```bash root@romulus:~# busctl tree xyz.openbmc_project.LED.GroupManager `- /xyz `- /xyz/openbmc_project `- /xyz/openbmc_project/led `- /xyz/openbmc_project/led/groups ``` :::info busctl:這是 Systemd 提供的工具,用於檢查與操作 D-Bus(Linux 程序間通訊的機制)。OpenBMC 極度依賴 D-Bus 來讓不同服務互相溝通。 tree:指令參數,要求以「樹狀圖」呈現,讓你一眼看出物件的層級關係。 xyz.openbmc_project.LED.GroupManager:這是服務的名稱(Service Name)。它代表負責管理 LED 群組(例如系統警告燈、定位燈等)的那個特定程式。 ::: 如果路徑出現成功出現就代表註冊成功,那我們就可以停掉剛剛放到背景執行的 phosphor-ledmanager,重新啟動系統來管理這個 service 先把手動跑的那個 process 關掉,否則會有兩個 phosphor-ledmanager 同時在跑,搶著註冊同一個 bus name xyz.openbmc_project.LED.GroupManager ```bash root@romulus:~# kill %1 ``` :::info kill %1:強制停止(結束)你剛才放在背景執行的第一個工作。 %1:在 Linux 終端機中,當執行指令並在結尾加上 &(例如你剛剛執行的 phosphor-ledmanager ... &),系統會將它放入背景,並分配一個編號(通常第一個就是 [1])。 ::: 建立一個特定的目錄 因為 systemd 規定,若要針對 xyz.openbmc_project.LED.GroupManager.service 進行「覆蓋(override)」而不修改原始設定的話,必須在 /etc/systemd/system/ 下建立一個以 .service.d 結尾的資料夾 ```bash root@romulus:~# mkdir -p /etc/systemd/system/xyz.openbmc_project.LED.GroupManager.service.d/ ``` 在剛剛建立的目錄底下,建立一個名為 override.conf 的設定檔,並寫入內容給 systemd 看 ```bash root@romulus:~# cat > /etc/systemd/system/xyz.openbmc_project.LED.GroupManager.service.d/override.conf << 'EOF' [Service] ExecStart= ExecStart=/usr/libexec/phosphor-led-manager/phosphor-ledmanager -c /tmp/led-empty.json EOF ``` :::info cat > [檔案路徑]:將接下來輸入的內容寫入指定的 override.conf。 << 'EOF':告訴系統:「開始讀取內容,直到我輸入 EOF 為止」。 'EOF' 加單引號:防止內容中的變數(如 $) 被當前的 Shell 提前解析。 ::: :::info [Service]:這是 systemd 設定檔的分段標籤,告訴系統接下來的設定是關於「服務如何執行」的參數。 ::: :::info ExecStart= 這兩行要連在一起看: 第一行 ExecStart=:在 systemd 中,如果你要更換一個 Type 不是 oneshot 的服務指令,必須先給一個空值來清除原有的指令。 第二行 ExecStart=/usr/... -c /tmp/led-empty.json:設定新的啟動指令。這裡加入了 -c /tmp/led-empty.json,強制服務去讀取那個空的 JSON 設定檔。 ::: 之後就讓 systemd 重新讀取所有 unit 檔和 override,改了設定之後一定要跑這個才會生效。 ```bash root@romulus:~# systemctl daemon-reload ``` 用新的設定重新啟動這個 service。 ```bash root@romulus:~# systemctl restart xyz.openbmc_project.LED.GroupManager.service ``` 重新確認 ```bash root@romulus:~# systemctl list-jobs --no-pager No jobs running. root@romulus:~# obmcutil state CurrentBMCState : xyz.openbmc_project.State.BMC.BMCState.Ready CurrentPowerState : xyz.openbmc_project.State.Chassis.PowerState.Off CurrentHostState : xyz.openbmc_project.State.Host.HostState.Off ``` 這樣 BMC 就成功啟動了!!! 最後再確認這台模擬的 BMC 可以透過網路被管理,從外面透過 SSH 連進 QEMU 模擬的 BMC,測試網路有沒有通 流程是: ``` Ubuntu 虛擬機 → SSH port 2222 → QEMU 的 user-mode network → BMC 內部 port 22 ``` ```bash $ ssh root@localhost -p 2222 root@localhost's password: root@romulus:~# ``` 輸入密碼登入後,會進入一個新的 shell,但這次是透過網路連進去的,不是直接在 QEMU console 上操作。 --- ## Sharing Downloads and Build Cache BitBake 的快取會同時 fetched sources (DL_DIR) and reusable build outputs (SSTATE_DIR),我們可以設定 BitBake 的共用快取,讓多個 build 可以共享已經下載和編譯過的東西。 :::info DL_DIR(Downloads) 存放 BitBake 從網路抓下來的原始碼,設定共用目錄後,換一個 machine(例如從 romulus 改 build 其他板子)就不用重新下載同樣的原始碼。 ::: :::info SSTATE_DIR(Shared State) 存放已經編譯好的中間產物,例如某個 library 編譯完的結果。下次 build 時如果這個 library 沒有變動,直接從 sstate 拿,不用重新編譯。 ::: 建立共用目錄 ```bash $ mkdir -p "${XDG_CACHE_HOME}/bitbake/downloads" "${XDG_CACHE_HOME}/bitbake/sstate" ``` 在每個 MACHINE 的 build/MACHINE_NAME/conf/local.conf 加上這兩條設定 ```bash DL_DIR = "${XDG_CACHE_HOME}/bitbake/downloads" SSTATE_DIR = "${XDG_CACHE_HOME}/bitbake/sstate" ``` --- ## OpenBMC 環境建置總結 整體流程 ``` Build 環境 → 編譯 Image → QEMU 模擬 → 驗證網路 ``` Build 階段重點 :::warning Ubuntu 24.04 需要關閉 AppArmor 的 unprivileged user namespace 限制,否則 BitBake worker 會失敗 ::: :::warning git clone 大型 repo 失敗時,強制改用 HTTP/1.1 可解決 HTTP/2 傳輸中斷問題 ::: OpenBMC 狀態層次,三個狀態代表不同層級,有依賴關係 :::info BMCState → BMC 本身是否 Ready(管理介面是否可用) PowerState → Chassis 電源是否接通 HostState → Host OS 是否在運作 BMC 是獨立運作的,不依賴 Host,開機後 BMCState 會從 NotReady 變成 Ready。 ::: D-Bus 與 systemd 的關係(核心概念:OpenBMC 的所有服務之間透過 D-Bus 溝通): :::info systemd 的 Type=dbus 表示服務啟動完成的條件是在 D-Bus 上成功註冊 bus name ::: :::info mapper-wait@<path>.service 是專門等待某個 D-Bus 路徑出現的機制,路徑沒出現就會卡住,導致後續所有依賴它的服務都 waiting ::: :::info busctl tree <service> 可以查看某個 D-Bus service 註冊了哪些物件路徑 ::: QEMU 模擬環境的限制 :::warning 部分服務依賴實體硬體(例如 LED、感測器),在 QEMU 裡會因為找不到硬體設定而無法啟動 解法是提供空的 config({})讓服務認為硬體不存在但仍能正常初始化 ::: :::warning 修改服務設定應使用 systemd override(.service.d/override.conf),不直接改原始 unit 檔,upgrade 時才不會被覆蓋 ::: 網路架構 | Host port | BMC port | 用途 | | --------- | -------- | ---------- | | 2222 | 22 | SSH | | 2443 | 443 | HTTPS/REST | | 2080 | 80 | HTTP | | 2623 | 623 | IPMI | 小於 1024 的 port(< 1024)需要 root 權限才能綁定,所以在一般使用者下執行 QEMU 時要改用高於 1024 的 port。

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully