郭鎮頴
    • 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
      • Invitee
      • No invitee
    • Publish Note

      Publish Note

      Everyone on the web can find and read all notes of this public team.
      Once published, notes can be searched and viewed by anyone online.
      See published notes
      Please check the box to agree to the Community Guidelines.
    • 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
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • 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 Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control 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
Invitee
No invitee
Publish Note

Publish Note

Everyone on the web can find and read all notes of this public team.
Once published, notes can be searched and viewed by anyone online.
See published notes
Please check the box to agree to the Community Guidelines.
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
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# 從頭打包Debian套件流程 # 前置作業 ## 了解套件狀態 可以到[WNPP](https://www.debian.org/devel/wnpp/)(Work-Needing and Prospective Packages)的列表查看套件的狀態是什麼。 也可以使用wnpp-check這個套件來查看 ``` $ wnpp-check [套件名] ``` ## 認領套件 認領套件的方法是把套件狀態改為ITP(Intent to Package),可以使用指令的方式`reportbug wnpp`,也可以使用發信的方式,下面會介紹寄信的方式。 認領套件有兩種情況: ### 套件並不存在WNPP上面 * 信件主旨格式:`ITP: [套件名] - 簡述` > 此處有誤,"-"應該要有兩個,如下 > ``` > ITP: [套件名] -- 簡述 > ``` > [name=Kaiden Yu] [time=2019, 10, 22 02:00 AM ] [color=#907bf7] * 收件者:`submit@bugs.debian.org` * 內容格式:要符合規範,下面給個範例 ``` # 這是註解:owner填上自己代表要打包 Package: wnpp Severity: wishlist Owner: Chen-Ying Kuo <evshary@gmail.com> * Package name : tzdiff Version : 0.9 Upstream Author : minmin <belgianbeer@aj.admwt.jp> * URL : https://github.com/belgianbeer/tzdiff * License : BSD-2-Clause Programming Lang: shell script Description : Show Timezone differences with local time in CLI (shell script). ``` ### 要認領既有套件 如果要認領既有套件其實很簡單,首先先找到你要認領套件的WNPP頁面,確定bug number。 * 信件主旨:沒有限制,可以一樣是`ITP: [套件名] - 簡述` > 此處有誤,"-"應該要有兩個,如下 > ``` > ITP: [套件名] -- 簡述 > ``` > [name=Kaiden Yu] [time=2019, 10, 22 02:00 AM ] [color=#907bf7] * 收件者:`[bug number]@bugs.debian.org` * 內容格式:主要是要使用Control指令改變title和owner,可參考下面範例 ``` # 這是註解:-1的地方代表延續bug number # 這是註解:retitle最主要是改變狀態,變成ITP # 這是註解:owner填上自己代表要打包 Control: retitle -1 ITP: checksec -- Bash script to test executable properties Control: owner -1 Chen-Ying Kuo <evshary@gmail.com> # 這是註解:可以表明自己要打包這個套件 I want to package this. ``` ## 安裝打包的必要套件 ``` $ sudo apt-get install build-essential fakeroot devscripts git git-buildpackage debmake lintian ``` 然而通常如果最後是要打包到SID版本(unstable Debian),可能結果會有些版本上的問題,所以建議可以安裝SID版本的套件。 1. 修改sources.list ``` $ vim /etc/apt/sources.list ``` 2. 加上如下這行 ``` $ deb http://ftp.de.debian.org/debian sid main ``` 3. 執行如下指令來安裝SID版本 ```sh $ sudo apt-get update # 確認可用版本 $ sudo apt-cache policy debhelper $ sudo apt-cache policy debmake $ sudo apt-cache policy lintian # 更新版本 $ sudo apt-get -t sid install debhelper debmake lintian ``` # 開始打包 ## 創造package repository Debian的套件都是放在一個叫做[Salsa](https://salsa.debian.org/)的GitLab上面,如果沒有帳號要申請一個,有的話就登入。 進入後就創造repo,名稱跟套件名一樣即可。如果DD已經有創造了,那你可以fork出來。 ## 產生package repository 1. 先clone剛剛在Salsa創立的repo ``` $ git clone [剛剛創立的repo] ``` 2. 產生debian/master的branch ``` $ cd [剛剛clone的repo] $ git checkout -b debian/master ``` 3. 先在上層目錄(與套件目錄平行)抓下upstream的tar檔 * 通常我們選擇upstream最新的release版本,名稱應該會是[版本].tar.gz 4. 利用tar檔產生初始branch,然後gbp會問你package的名稱 ``` $ gbp import-orig --pristine-tar ../[版本].tar.gz --debian-branch=debian/master -u [版本] ``` 5. 完成後就是初步的套件了 * repo裡面會有三個branch(debian/master, pristine-tar, upstream) * 外層資料夾會有[package_name]_[version].orig.tar.gz的檔案 * 這三個branch分別代表的意義如下 1. upstream: 原作者的source 2. pristine-tar: 利用原作者的source產生出來的tar檔 3. debian/master: 除了upstream的source外,還包括debian的資料夾,這也是我們主要修改上傳資訊的地方 ## 產生Debian套件所需內容 接著我們會執行debmake,產生debian的資料夾,裡面的相關檔案就是打包套件所需的資訊 ``` # 把剛剛創的資料夾加上版號 $ mv [repo] [repo]_[版號] $ cd [repo]_[版號] $ debmake ``` > 此處有誤,應將 "_" 改為 "-" ,如下 > ``` > mv [repo] [repo]-[版號] > cd [repo]-[版號] > ``` > [name=Kaiden Yu] [time=2019, 06, 22 11:00 PM ] [color=#907bf7] ## 修改debian相關檔案 修改的規則可參考[Debian Policy Manual](https://www.debian.org/doc/debian-policy/),下面記錄的內容僅供參考,因為policy會變,還是要以manual為主。 以下是需要修改的檔案。 ### debian/copyright copyright格式要小心,必須按照原作者的copyright,通常demake會幫忙判斷版權,不過如果有錯誤還是得自己修正。 如果有原作者自己定義的License的話,可以參考[zsh](https://salsa.debian.org/debian/zsh)的改法 幾個可參考的連結 * https://salsa.debian.org/debian/nnn/blob/debian/master/debian/copyright * https://salsa.debian.org/debian/skypat/blob/debian/master/debian/copyright * https://gist.github.com/kasramp/3ef3e1e4b323d8b3a74551e688c2988f ### debian/control 1. Section選擇應有分類,選misc,參考[官網分類](https://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections) 2. 其餘email名字也要修改,並且放上官網和敘述 3. 如果有套件相依性的話,需要在Depends上面加上去,然後跑debuild和pubuilder的時候應該就會自己裝了。 - 不過有些套件不用寫上去,除了沒用到的以外還有essential,可用這個指令`dpkg-query -Wf '${Package;-40}${Essential}\n' | grep yes`確定哪些是essential,可參考[Finding all “essential” packages with apt](https://unix.stackexchange.com/questions/73242/finding-all-essential-packages-with-apt) - 如果不清楚相依套件或版本的話,可能就需要詢問上游了。不過如果沒有特別需求,版本一般可以不用填。 - 有些shell裡面會寫有目前使用的指令,如果要反查是什麼套件的話可用如下指令(參考自[Command to find the source package of a binary?](https://superuser.com/questions/146875/command-to-find-the-source-package-of-a-binary)) ``` dpkg -S `which COMMANDHERE` ``` 4. 記得要補上VCS ### debian/changelog 這邊會用到[dch](https://www.commandlinux.com/man-page/man1/dch.1.html),可使用`dch -i`來修改changelog檔案,基本上一次上傳只進一版(第一次的上傳所有修改都只會是x.x.x-1) 有幾個地方要修改一下 * unknown要改成unstable * bug number要改為自己的ITP number * 修改自己的名字、email ### debian/rules 如果原始程式碼的Makefile並不是我們要的,例如說會install檔案到`/usr/local/bin`(我們沒有權限可以做這件事),那應該就必須要修改這個檔案。 記住要把debmake產生的註解拿掉。 ### debian/patch - 若有更動原始碼的需求,使用gbp打patch,記得加上no-patch-num,來去除patch number ``` $ gbp clone --pristine-tar [Salsa的git位置] delay_package_gbm $ gbp pq import --no-patch-num # 此時會切換到patch branch,開始做必要的更動並commit $ gbp pq export --no-patch-num ``` ### debian/[pkg_name].manpages - 若無manpage,請在github上開issue請原作者加入,或參考別人的package加入後回饋給上游 - `[pkg_name].manpages`內新增manpage的位置,例如`[package].1` - 如果想要看manpage長什麼樣子,可用`nroff -man [package].1` ### debian/tests 實際上怎麼修改可以參考[4. autopkgtest: Automatic testing for packages](http://packaging.ubuntu.com/html/auto-pkg-test.html)和[Tutorial: Functional Testing of Debian Packages](https://annex.debconf.org/debconf-share/debconf15/slides/173-tutorial-functional-testing-of-debian-packages.pdf) 1. 首先先在debian底下新增tests資料夾 ``` mkdir tests ``` 2. tests底下增加control這個檔案,內容如下 - Tests右邊的build代表我們要測試的script file - Depends右邊的@代表相依檔案只有自己,如果有其他就加在這行 - 可參考[別人的範例](https://github.com/HypoPG/hypopg/blob/master/debian/tests/control) ``` Tests: build Depends: @ ``` 3. 接著新增build這個檔案 ```sh #!/bin/sh set -e echo "run start" # 要做的測試 echo "run: OK" ``` ### debian/watch 1. 修改watch的檔案,可參考[官網介紹](https://wiki.debian.org/debian/watch)或[這邊的範例](https://salsa.debian.org/debian/buku/blob/debian/master/debian/watch) ``` version=4 opts=\ repacksuffix=+ds,\ repack,compression=xz,\ dversionmangle=s/\+(debian|dfsg|ds|deb)(\.?\d+)?$//,\ filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%<project>-$1.tar.gz% \ https://github.com/jarun/Buku/releases \ (?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate ``` 2. 可用uscan測試 ``` # 測試方式 uscan --no-download --verbose # 如果要debug uscan --no-download --verbose --debug ``` ### debian/upstream 1. 在debian資料夾下新增upstream資料夾 ``` mkdir upstream ``` 2. 在upstream資料夾中新增metadata ``` cd upstream vi metadata ``` 3. metadata中增加相關資訊,可參考[別人的範例](https://salsa.debian.org/debian/skypat/blob/debian/master/debian/upstream/metadata) ``` Bug-Database: https://github.com/skymizer/SkyPat/issues Bug-Submit: https://github.com/skymizer/SkyPat/issues/new Name: skypat Repository: https://github.com/skymizer/SkyPat.git Repository-Browse: https://github.com/skymizer/SkyPat ``` ### debian/install 1. 在debian資料夾下新增install這個檔案 ``` vi install ``` 2. 增加要搬移的位置,通常為usr/bin ``` [執行檔] usr/bin ``` ### debian/[package_name].docs 要記得創立debian/[package_name].docs,主要是要把README.md包入binary package內 最後修改完相關debian檔案後,git log要整理,第一次上套件的log只留一筆,內容可以寫"Initialize packaging"。 ## 執行debuild 改完後就可以開始用debuild了,不過我們不是DD,所以可以跳過sign key的動作。 修改`~/.devscripts`或`/etc/devscripts.conf`為如下 ``` DEBUILD_LINTIAN=yes # -E include experimental tags DEBUILD_LINTIAN_OPTS="-i -EvIL +pedantic --show-overrides" #-uc -us代表不要sign key DEBUILD_DPKG_BUILDPACKAGE_OPTS="-uc -us --changes-option=-sa" # DEBSIGN_KEYID可填可不填 DEBSIGN_KEYID="FIXME - YOUR KEYID" DEB_BUILD_OPTIONS="parallel=10" ``` 執行完成後會在自己資料夾的上一層產生許多檔案,包括[套件名稱].dsc,這就是所謂的Debian source control files,之後如果要跑pubuilder也會需要用到。 ``` $ debuild ``` ### 清除debuild產生的檔案 因為debuild會產生許多檔案,這些都是不用上git的。如果想要清除的話,可以用如下指令 ``` debuild -- clean ``` ### 解決Lintian的問題 跑完後可能會出現Lintian相關的錯誤,相關錯誤的解法可參考[Solutions to some lintian warnings](https://hackmd.io/tsRaPMWTQACNbFZ5v05O-w) # 驗證修改 為了確認打包結果是否可以在一個乾淨的環境上執行,以及是否能通用在32, 64bit的系統上,所以要經過下面的測試。 ## 使用pbuilder建立sid環境 使用pubuilder最主要的原因是debuild是在我們當前環境,可能不夠乾淨,pbuilder可以模擬一個乾淨的環境供我們測試是否有相依性或其他問題。 1. 安裝pbuilder後,首先設定`/etc/pbuilderrc` ```sh # this is your configuration file for pbuilder. # the file in /usr/share/pbuilder/pbuilderrc is the default template. # /etc/pbuilderrc is the one meant for overwriting defaults in # the default template # # read pbuilderrc.5 document for notes on specific options. MIRRORSITE=http://deb.debian.org/debian # the hook dir may already be set/populated! HOOKDIR="/var/cache/pbuilder/hooks/" if [ -n "$DEPS" ] ; then export DEPSBASE=/var/cache/pbuilder/deps BINDMOUNTS=$DEPSBASE fi ``` 2. pbuilder執行時會執行hook資料夾下設定的hook,通常我們會加入一個Lintian hook來做檢查。在`/var/cache/pbuilder/hooks/B90lintian`中輸入下面內容,相關example可在`/usr/share/doc/pbuilder/examples`找到。 P.S. 每個不同功能的hook都有制式的名字,不能隨意輸入 ```dash #!/bin/dash set -e install_packages() { apt-get -y --allow-downgrades install "$@" } install_packages lintian echo "+++ lintian output +++" su -c "lintian -i -EvIL +pedantic --show-overrides /tmp/buildd/*.changes" - pbuilder # use this version if you don't want lintian to fail the build #su -c "lintian -i -I --show-overrides /tmp/buildd/*.changes; :" - pbuilder echo "+++ end of lintian output +++" ``` 3. 記得把`/var/cache/pbuilder/hooks/B90lintian`執行權限打開 ```bash chmod a+x /var/cache/pbuilder/hooks/B90lintian ``` 4. 由於我們要在SID的環境執行(也就是不穩定的Debian版本),可參考[這裡的設定](https://wiki.debian.org/PbuilderTricks),先創立`~/.pbuilderrc`(通常應該是`/root/.pbuilderrc`),然後貼上如下內容 ```dash # Codenames for Debian suites according to their alias. Update these when # needed. UNSTABLE_CODENAME="sid" TESTING_CODENAME="buster" STABLE_CODENAME="stretch" STABLE_BACKPORTS_SUITE="$STABLE_CODENAME-backports" # List of Debian suites. DEBIAN_SUITES=($UNSTABLE_CODENAME $TESTING_CODENAME $STABLE_CODENAME $STABLE_BACKPORTS_SUITE "experimental" "unstable" "testing" "stable") # List of Ubuntu suites. Update these when needed. UBUNTU_SUITES=("xenial" "wily" "vivid" "utopic" "trusty") # Mirrors to use. Update these to your preferred mirror. DEBIAN_MIRROR="deb.debian.org" UBUNTU_MIRROR="mirrors.kernel.org" # Optionally use the changelog of a package to determine the suite to use if # none set. if [ -z "${DIST}" ] && [ -r "debian/changelog" ]; then DIST=$(dpkg-parsechangelog --show-field=Distribution) fi # Optionally set a default distribution if none is used. Note that you can set # your own default (i.e. ${DIST:="unstable"}). : ${DIST:="$(lsb_release --short --codename)"} # Optionally change Debian codenames in $DIST to their aliases. case "$DIST" in $UNSTABLE_CODENAME) DIST="unstable" ;; $TESTING_CODENAME) DIST="testing" ;; $STABLE_CODENAME) DIST="stable" ;; esac # Optionally set the architecture to the host architecture if none set. Note # that you can set your own default (i.e. ${ARCH:="i386"}). : ${ARCH:="$(dpkg --print-architecture)"} NAME="$DIST" if [ -n "${ARCH}" ]; then NAME="$NAME-$ARCH" DEBOOTSTRAPOPTS=("--arch" "$ARCH" "${DEBOOTSTRAPOPTS[@]}") fi BASETGZ="/var/cache/pbuilder/$NAME-base.tgz" DISTRIBUTION="$DIST" BUILDRESULT="/var/cache/pbuilder/$NAME/result/" APTCACHE="/var/cache/pbuilder/$NAME/aptcache/" BUILDPLACE="/var/cache/pbuilder/build/" HOOKDIR="/var/cache/pbuilder/hooks/" if $(echo ${DEBIAN_SUITES[@]} | grep -q $DIST); then # Debian configuration MIRRORSITE="http://$DEBIAN_MIRROR/debian/" COMPONENTS="main contrib non-free" if $(echo "$STABLE_CODENAME stable" | grep -q $DIST); then OTHERMIRROR="$OTHERMIRROR | deb $MIRRORSITE $STABLE_BACKPORTS_SUITE $COMPONENTS" fi elif $(echo ${UBUNTU_SUITES[@]} | grep -q $DIST); then # Ubuntu configuration MIRRORSITE="http://$UBUNTU_MIRROR/ubuntu/" COMPONENTS="main restricted universe multiverse" else echo "Unknown distribution: $DIST" exit 1 fi ``` 5. 建立pbuilder環境 ``` $ sudo OS=debian DIST=sid ARCH=i386 pbuilder create $ sudo OS=debian DIST=sid ARCH=amd64 pbuilder create ``` 6. 執行pbuilder ``` # 注意若changelog版本號有變,xxx.dsc的檔案名也要更動 $ sudo OS=debian DIST=sid ARCH=i386 pbuilder build [剛剛產生的的dsc檔].dsc $ sudo OS=debian DIST=sid ARCH=amd64 pbuilder build [剛剛產生的的dsc檔].dsc # 若有錯誤,修正後先跑debuild然後再重複上面pbuilder build的步驟 $ debuild ``` 7. 若有更動/root/.pbuilderrc,記得更新pbuilder環境 ```sh $ sudo OS=debian DIST=sid ARCH=i386 pbuilder update --override-config --configfile ~/.pbuilderrc $ sudo OS=debian DIST=sid ARCH=amd64 pbuilder update --override-config --configfile ~/.pbuilderrc ``` ## 一切完成後 1. commit並推上自己在Salsa的repository 3. 請認識的Debian Developer來clone repository,或是發MR到已經存在的repository,再請他們幫忙打包上傳 ## 注意事項 - 在製作deb前確認原始碼與環境(可重製環境)乾淨 - 記得跑lintian檢查製作的.dsc檔案是否正確 - 當修正並非特別為Debian或Linux時,記得將patch送回上游進行反饋 # 更新套件 要更新套件的話,我們需要更新三個branch並且發MR出去,三個branch分別為debian/master, pristine-tar和upstream。 **不過要特別注意自己fork的repository和目前在Salsa上的repository有沒有同步,如果沒有要先做好同步才行。** ## 從原作者的repository更新 如果我們有設定 watch 的話,可以直接透過 gbp 指令來更新,可參考 [New upstream release](https://wiki.debian.org/Python/GitPackaging?action=show&redirect=Python%2FGitPackagingPQ#New_upstream_release) - `gbp pq` 相關操作可參考[這邊](http://honk.sigxcpu.org/projects/git-buildpackage/manual-html/man.gbp.pq.html) - `gbp import-orig` 相關操作可參考[這邊](http://honk.sigxcpu.org/projects/git-buildpackage/manual-html/man.gbp.import.orig.html) - 在執行前確認自己 local 端都已經有相對應的 branch,包括 debian/master, pristine-tar, upstream ```sh # 從 upstream 更新程式碼到 pristine-tar 和 debian branch,並且指定 local 的 debian branch 為 debian/master $ gbp import-orig --pristine-tar --uscan --debian-branch=debian/master # 如果跟 upstream 有衝突的話,可以用 --merge-mode=replace 強制使用 upstream commit $ gbp import-orig --pristine-tar --uscan --debian-branch=debian/master --merge-mode=replace ``` 完成後就可以切回 debian/master branch 並做後續的修改了。 ### 更新changelog 一樣可以用gbp更新changelog(記得要把UNRELEASED改為unstable) ``` gbp dch --debian-branch=debian/master ``` 要特別注意的是,changelog上面要紀錄你對debian file的所有修改(不是upstream的部份)。 ### 如果不是upstream更新,而是debian相關的進版 * 如果上層目錄沒有tar檔的話,先用pristine-tar來取得tar檔 ``` # 目前有哪些tar檔 pristine-tar list # 選擇最新的tar檔 pristine-tar checkout <tar> # 把tar檔移到上層 mv <tar> .. ``` * 更新changelog ``` dch -i ``` ### 確認debian/control和debian/compat 更新套件的時候要注意control裡面的資訊有沒有過時,若沒有更新,在跑pbuilder應該就會跳出警告了。 * Build-Depends:這個跟debhelper版本有關,記得更新完也要更新compat * Standards-Version:這個跟debmake有關 如果發現自己local端的debhelper和debmake過時,那可以用前面安裝SID的方式更新。 ### 發Merge Request 當套件都更新完後,一樣要用debuild和pbuilder再跑一次,確認沒問題,然後就可以把debian/master、pristine-tar和upstream都push上去Salsa。 記得三個branch都要發MR給DD。 # 參考資源 * [Debian 新維護人員手冊](https://www.debian.org/doc/manuals/maint-guide/index.zh-tw.html):本文只能簡單帶過,如果遇到其他問題還是建議參考這份文件。 * [配合Git为Debian系发行版打包的正确方式](https://hosiet.me/blog/2016/09/15/make-debian-package-with-git-the-canonical-way/): 非常推薦看這份文件,寫得很詳細。 * [Debian 维护者指南](https://www.debian.org/doc/manuals/debmake-doc/debmake-doc.zh-cn.pdf) * [Debian Policy Manual](https://www.debian.org/doc/debian-policy/index.html)

Import from clipboard

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 is not available.
Upgrade
All
  • All
  • Team
No template found.

Create custom 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

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

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

Documents

Tutorials

Book Mode Tutorial

Slide Mode Tutorial

Contacts

Facebook

Twitter

Discord

Feedback

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
Upgrade to Prime Plan

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

No updates to save
Compare with
    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

      Upgrade

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Upgrade

      Danger Zone

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

      Syncing

      Push failed

      Push successfully