├── .gitignore ├── Makefile ├── README.md ├── make.bat ├── preview.png ├── requirements.txt └── source ├── branchingmodel.rst ├── commands.rst ├── conf.py ├── configurations.rst ├── conflict.rst ├── gitlab.rst ├── gitlabworkflow.rst ├── images ├── GitLab │ ├── create_project1.png │ ├── create_project2.png │ ├── create_project3.png │ ├── gitlab_project_tree.png │ ├── import_pubkey1.png │ ├── import_pubkey2.png │ └── public_access.png ├── after_rebase_onto.png ├── before_rebase_onto.png ├── file_status.png ├── git-branching-model.png ├── master_branch.png ├── merge-without-ff.png ├── merge.png ├── rebase.png ├── rebase_feature_a.png └── rebase_feature_b.png ├── index.rst ├── installation.rst ├── other.rst ├── rebase.rst ├── references.rst └── tagging.rst /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Git.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Git.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Git" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Git" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **本系列文章以移至 [https://myapollo.com.tw/series/git-tutorial](https://myapollo.com.tw/series/git-tutorial)** 2 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | goto end 41 | ) 42 | 43 | if "%1" == "clean" ( 44 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 45 | del /q /s %BUILDDIR%\* 46 | goto end 47 | ) 48 | 49 | 50 | %SPHINXBUILD% 2> nul 51 | if errorlevel 9009 ( 52 | echo. 53 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 54 | echo.installed, then set the SPHINXBUILD environment variable to point 55 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 56 | echo.may add the Sphinx directory to PATH. 57 | echo. 58 | echo.If you don't have Sphinx installed, grab it from 59 | echo.http://sphinx-doc.org/ 60 | exit /b 1 61 | ) 62 | 63 | if "%1" == "html" ( 64 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 68 | goto end 69 | ) 70 | 71 | if "%1" == "dirhtml" ( 72 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 76 | goto end 77 | ) 78 | 79 | if "%1" == "singlehtml" ( 80 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 84 | goto end 85 | ) 86 | 87 | if "%1" == "pickle" ( 88 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can process the pickle files. 92 | goto end 93 | ) 94 | 95 | if "%1" == "json" ( 96 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 97 | if errorlevel 1 exit /b 1 98 | echo. 99 | echo.Build finished; now you can process the JSON files. 100 | goto end 101 | ) 102 | 103 | if "%1" == "htmlhelp" ( 104 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 105 | if errorlevel 1 exit /b 1 106 | echo. 107 | echo.Build finished; now you can run HTML Help Workshop with the ^ 108 | .hhp project file in %BUILDDIR%/htmlhelp. 109 | goto end 110 | ) 111 | 112 | if "%1" == "qthelp" ( 113 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 117 | .qhcp project file in %BUILDDIR%/qthelp, like this: 118 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Git.qhcp 119 | echo.To view the help file: 120 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Git.ghc 121 | goto end 122 | ) 123 | 124 | if "%1" == "devhelp" ( 125 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished. 129 | goto end 130 | ) 131 | 132 | if "%1" == "epub" ( 133 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 137 | goto end 138 | ) 139 | 140 | if "%1" == "latex" ( 141 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 145 | goto end 146 | ) 147 | 148 | if "%1" == "latexpdf" ( 149 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 150 | cd %BUILDDIR%/latex 151 | make all-pdf 152 | cd %BUILDDIR%/.. 153 | echo. 154 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 155 | goto end 156 | ) 157 | 158 | if "%1" == "latexpdfja" ( 159 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 160 | cd %BUILDDIR%/latex 161 | make all-pdf-ja 162 | cd %BUILDDIR%/.. 163 | echo. 164 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 165 | goto end 166 | ) 167 | 168 | if "%1" == "text" ( 169 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 170 | if errorlevel 1 exit /b 1 171 | echo. 172 | echo.Build finished. The text files are in %BUILDDIR%/text. 173 | goto end 174 | ) 175 | 176 | if "%1" == "man" ( 177 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 178 | if errorlevel 1 exit /b 1 179 | echo. 180 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 181 | goto end 182 | ) 183 | 184 | if "%1" == "texinfo" ( 185 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 186 | if errorlevel 1 exit /b 1 187 | echo. 188 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 189 | goto end 190 | ) 191 | 192 | if "%1" == "gettext" ( 193 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 194 | if errorlevel 1 exit /b 1 195 | echo. 196 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 197 | goto end 198 | ) 199 | 200 | if "%1" == "changes" ( 201 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 202 | if errorlevel 1 exit /b 1 203 | echo. 204 | echo.The overview file is in %BUILDDIR%/changes. 205 | goto end 206 | ) 207 | 208 | if "%1" == "linkcheck" ( 209 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 210 | if errorlevel 1 exit /b 1 211 | echo. 212 | echo.Link check complete; look for any errors in the above output ^ 213 | or in %BUILDDIR%/linkcheck/output.txt. 214 | goto end 215 | ) 216 | 217 | if "%1" == "doctest" ( 218 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 219 | if errorlevel 1 exit /b 1 220 | echo. 221 | echo.Testing of doctests in the sources finished, look at the ^ 222 | results in %BUILDDIR%/doctest/output.txt. 223 | goto end 224 | ) 225 | 226 | if "%1" == "xml" ( 227 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 228 | if errorlevel 1 exit /b 1 229 | echo. 230 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 231 | goto end 232 | ) 233 | 234 | if "%1" == "pseudoxml" ( 235 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 236 | if errorlevel 1 exit /b 1 237 | echo. 238 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 239 | goto end 240 | ) 241 | 242 | :end 243 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/preview.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2==2.7.2 2 | MarkupSafe==0.23 3 | Pygments==1.6 4 | Sphinx==1.2.2 5 | docutils==0.11 6 | sphinx-rtd-theme==0.1.6 7 | wsgiref==0.1.2 8 | -------------------------------------------------------------------------------- /source/branchingmodel.rst: -------------------------------------------------------------------------------- 1 | Git flow 分支策略 2 | ========================== 3 | 4 | 在本文中,我們依據 `A successful Git branching model `_ 5 | 一文展示一種開發時可以應用的分支策略模型,不論是大型專案或者小型應用程式,此模型皆可套用於其中,並享受到導入此分支策略模型所帶來的成功。在文章裡有關專案(project)的任何細節將不被談論,只談論分支(branching)的策略及發佈(release)的管理。下圖即是整個分支策略的概觀。後續小節將敘述相關分支策略的細節。 6 | 7 | .. figure:: images/git-branching-model.png 8 | :align: center 9 | 10 | Git branching model [Ref1]_ 11 | 12 | ========================= 13 | 主分支(Master) 14 | ========================= 15 | 16 | Git branching model 一圖中最主要的分支有兩大個,主分支(master)與開發分支(develop)。這兩個分支原則上一直都存在於版本控制的repository中,而且各司其職。以下將分別介紹兩大分支: 17 | 18 | - 主分支(master):處於production-ready的狀態,換句話說,即是該版的source 19 | code是可運行的、符合專案需求的、設計良好的、穩定的、可維護的、可擴展的及已文件化的。 [Ref3]_ 20 | 21 | - 開發分支(develop):下次發佈版本的最新狀態。從主要分支分出來。有些開發者也稱開發分支為整合分支,自動化測試所根據的程式碼(source code)即是以此分支上的版本為基準來進行測試。 22 | 23 | 當開發分支上的source code達到一定穩定程度,並準備發佈時,即可將該分支上的變更合併回主要分支,並標上發佈的版本號。這就是所謂的新產品發佈。以下將逐一探討實作細節。 24 | 25 | .. [Ref3] Define “production-ready”, http://programmers.stackexchange.com/questions/61726/define-production-ready 26 | 27 | 28 | ================================================ 29 | 支援分支(feature, release, hotfix) 30 | ================================================ 31 | 32 | 除了主分支,我們也會使用支援分支來幫助專案開發。支援分支可讓整個團隊更容易管理新功能的開發、產品發佈分支或是快速修改一些bug。支援分支與主要分支最大的差別在於,支援分支在支援任務結束後就會移除,而主要分支則是始終存在。 33 | 34 | 本文中我們所使用到的支援分支有:功能分支(feature)、發佈分支(release)、修補分支(hotfix)。每一個支援分支都有其特殊支援目的,並嚴格遵循分支與合併的規定。例如:功能分支(feature)只能從開發分支(develop)分離出來,也只能與開發分支(develop)合併。 35 | 36 | 37 | 功能分支 (feature branch) 38 | ------------------------------------------------ 39 | 40 | 功能分支原則: 41 | 42 | - 從開發分支分離。 43 | - 合併回開發分支。 44 | - 分支命名規則:除了master, develop, release-*, or hotfix-* 以外的功能名稱都行。 45 | 46 | 功能分支顧名思義就是開發新功能的分支。只要新功能未開發完成,功能分支就會持續存在直到開發完成並合併回開發分支或直到放棄開發此新功能。另外,此分支通常只會存在於該功能的開發者的本機端的儲存庫(local repository),遠端的中央儲存庫(remote repository 或者 remote origin)是不會有的。 47 | 48 | 開功能分支詳細步驟: 49 | 50 | 1. 開「功能」分支 :: 51 | 52 | # 從開發分支開一名為「myfeature」的分支,開完後切換到myfeature分支 53 | $ git checkout -b myfeature develop 54 | 55 | #. 將已開發完成之功能分支合併回develop分支 :: 56 | 57 | # 切換至開發分支 58 | $ git checkout develop 59 | 60 | # 將myfeature分支合併到開發分支 61 | $ git merge --no-ff myfeature 62 | 63 | # 刪除myfeature分支 64 | $ git branch -d myfeature 65 | 66 | # 將開發分支push到遠端的origin 67 | $ git push origin develop 68 | 69 | ``--no-ff`` 參數可以保存myfeaute分支上的歷史記錄,讓開發者可以更了解開發的來龍去脈。詳情如下圖所示: 70 | 71 | .. figure:: images/merge-without-ff.png 72 | :align: center 73 | 74 | ``--no-ff`` 參數示意圖 [Ref1]_ 75 | 76 | 右圖中是沒有加入 ``--no-ff`` 的情況,所以無法看見哪些提交(commit)是專門因為開發新功能而產生的,若有追蹤程式碼的需求時,開發者必須得要自行閱讀所有的log訊息才能知道問題也許出在哪一個版本,相較之下較為不便。 77 | 78 | 發佈分支 (release branch) 79 | -------------------------------------------------- 80 | 81 | 發佈分支原則: 82 | 83 | - 從開發分支分離。 84 | - 合併回開發分支或主要分支。 85 | - 分支命名規則:release-* 86 | 87 | 發佈分支是為了新版本發佈而存在的,換言之,可以說是將程式碼與主分支(master)合併前的過渡階段,可以想像成在發佈新版本之前所必須進行的相關手續。例如在此分支必須進行一些整合性測試,並進行小bug修復及增加一些metadata(例如版本號或是build日期等)。有了發佈分支,開發分支就可以只需專注接收功能分支(feature)所合併進來的功能即可。 88 | 89 | 一般而言,當產品開發到一個階段時,發佈產品的需求就會自然顯露出。此時,即是開發佈分支的關鍵時刻。在此階段,所有為了這一次發佈所開發的新功能就需要合併到開發分支(develop),接著再由開發分支(develop)分出發佈分支,開始進行相關發佈前的手續。 90 | 91 | 制定版本號碼的最佳時機是在開發佈分支時。直到發佈分支之前,開發分支上有許多「為了下一次發佈」所合併的新功能,但對於版本號是0.3或1.0尚無法確認。而發佈分支便是為了確認版本號等其他事情而開啟的,版本號碼的制定從開分支時就需制定且遵循專案版本號制定規則。 92 | 93 | 發佈分支詳細步驟: 94 | 95 | 1. 開「發佈」分支 :: 96 | 97 | # 從開發分支開一支名為「release-1.2」的分支,開完後切換到release-1.2分支。 98 | $ git checkout -b release-1.2 develop 99 | 100 | #. 制定版本號 :: 101 | 102 | # commit 一個版本,commit 訊息為「版本號跳躍至1.2」 103 | $ git commit -a -m "Bumped version number to 1.2" 104 | 105 | #. 將已制定好metadata或已修復小bug的發佈分支合併到主要分支 :: 106 | 107 | # 切換至主要分支 108 | $ git checkout master 109 | 110 | # 將release-1.2分支合併到主要分支 111 | $ git merge --no-ff release-1.2 112 | 113 | # 上tag 114 | $ git tag -a 1.2 115 | 116 | #. 將已制定好metadata或已修復小bug的發佈分支合併回開發分支 :: 117 | 118 | # 切換至開發分支 119 | $ git checkout develop 120 | 121 | # 將release-1.2分支合併回開發分支 122 | $ git merge --no-ff release-1.2 123 | 124 | #. 刪除release-1.2分支 :: 125 | 126 | $ git branch -d release-1.2 127 | 128 | 129 | 修補程式分支 (hotfix) 130 | ---------------------------------------------- 131 | 132 | 修補程式分支原則: 133 | 134 | - 從主分支分離。 135 | - 合併回開發分支或主分支。 136 | - 分支命名規則:hotfix-* 137 | 138 | 修補程式分支的誕生通常是因為出現了一個急需在短時間內修復的bug,無法等到下一次發佈時才修復,例如與伺服器間的通訊有bug,需要馬上進行更新修正。此分支通常從主分支某個已標上tag的commit分出,bug經過修復後,可合併到開發分支,或者是合併回主分支,並標上另一版本號的tag。開此分支的目的在於開發分支上的成員可以繼續原本的工作,而bug修復工作也同時由另外的成員來執行,使得對彼此間的影響降到最低。 139 | 140 | 開修補程式分支詳細步驟: 141 | 142 | 1. 開「修補程式」分支 (hotfix branch) :: 143 | 144 | # 從主要分支開一支名為「hotfix-1.2.1」的分支,開完後切換到hotfix-1.2.1分支。 145 | $ git checkout -b hotfix-1.2.1 master 146 | 147 | 2. 制定版本號 :: 148 | 149 | # commit 一個版本,commit 訊息為「版本號跳躍至1.2.1」 150 | $ git commit -a -m "Bumped version number to 1.2.1" 151 | 152 | 3. 修正bug並commit一版 :: 153 | 154 | $ git commit -m "Fixed severe production problem" 155 | 156 | 4. 將修正完bug的修補程式分支合併回主分支 :: 157 | 158 | # 切換至主要分支 159 | $ git checkout master 160 | 161 | # 將hotfix-1.2.1分支合併到主要分支 162 | $ git merge --no-ff hotfix-1.2.1 163 | 164 | # 上tag 165 | $ git tag -a 1.2.1 166 | 167 | 5. 將修復完成的修補程式分支合併回開發分支 :: 168 | 169 | # 切換至開發分支 170 | $ git checkout develop 171 | 172 | # 將hotfix-1.2.1分支合併回開發分支 173 | $ git merge --no-ff hotfix-1.2.1 174 | 175 | 需要特別注意的是,若修補程式分支與發佈分支同時存在。則當bug修補已完成時,就不是合併回開發分支,而是發佈分支。修補程式就會在從未來發佈分支合併回開發分支時一併將bug修補完。 176 | 177 | 6. 刪除hotfix-1.2.1分支 :: 178 | 179 | $ git branch -d hotfix-1.2.1 180 | 181 | 182 | 以上就是 Git flow 分支策略的詳細說明。當然,本文所解說的也只是一種策略的框架,不一定要百分之百依循,也可以隨自己的喜好化用出不同的策略模型。 183 | -------------------------------------------------------------------------------- /source/commands.rst: -------------------------------------------------------------------------------- 1 | Git必懂指令 2 | =========== 3 | 4 | 瞭解使用Git時所必備的指令,可以從單人開發及多人合作開發的情境著手。 5 | 6 | 單人開發的情境可以了解如何利用Git從無到有建立具版本控制的開發流程;多人合作開發的情境能夠了解如何透過Git在不影響開發環境之下與其他團隊成員共同協作。 7 | 8 | 在開始之前,有一點觀念必須釐清。版本的演進,是線性的演進,因此我們通常會有一個稱為 ``master`` 的主線。而開發過程所發現的issue或是需要新增的功能,我們會希望在不影響 ``master`` 的情況下,複製 ``master`` 衍生出一個分支(branch)進行開發,等到分支開發完成、測試穩定之後,才會與 ``master`` 主線合併(merge),演化出全新的版本,如下圖所示。 9 | 10 | .. figure:: images/master_branch.png 11 | :align: center 12 | 13 | 線性的演化 [Ref1]_ 14 | 15 | .. [Ref1] A successful Git branching model, http://nvie.com/posts/a-successful-git-branching-model/ 16 | 17 | =========================== 18 | 單人開發模式 - 新增篇 19 | =========================== 20 | 21 | 本章節將會介紹在單人開發模式之下所需要的幾個重要指令。 22 | 23 | 首先,建立一個專案資料夾 ``gittest``,我們將對這個專案資料夾裡面的所有檔案進行版本控制。 :: 24 | 25 | $ mkdir gittest 26 | 27 | 接著,我們必須指定Git對這個專案資料夾進行版本控制,指令形式為 ``git init [資料夾名稱]`` 。 :: 28 | 29 | $ git init gittest 30 | 31 | 上述指令成功之後,系統會顯示類似以下的訊息,可以看到Git會在專案資料夾內建立一個 ``.git`` 的隱藏資料夾。事實上,Git就是將所有的版本控制相關的資訊儲存於此一隱藏資料夾內,因此這個資料夾不應被刪除。 :: 32 | 33 | Initialized empty Git repository in /home/vans/gittest/.git/ 34 | 35 | 再來,我們試圖模擬一般的程式開發過程,在這個資料夾內新增兩個檔案 ``README`` 與 ``HelloWorld.c`` 。 :: 36 | 37 | $ touch README HelloWorld.c 38 | 39 | 新增檔案之後,我們仍需要知道這些檔案的在版本控制系統內的狀態 *(untracked, unmodified, modified, staged)* ,以確定哪些檔案被新增、變更、刪除等,便於進行後續的管理,例如告知Git需要將新增的檔案納入版本控制、接受變更等。察看檔案狀態的指令為 ``git status`` 。 :: 40 | 41 | $ git status 42 | # On branch master 43 | # 44 | # Initial commit 45 | # 46 | # Untracked files: 47 | # (use "git add ..." to include in what will be committed) 48 | # 49 | # HelloWorld.c 50 | # README 51 | nothing added to commit but untracked files present (use "git add" to track) 52 | 53 | 上述的指令顯示了幾項資訊,分述如下: 54 | 55 | #. On branch master, 位於名為 ``master`` 的分支 56 | #. Initial commit, 目前尚未有第一次的 ``commit`` (提交變更) 57 | #. Untracked files, 顯示 ``HelloWorld.c`` , ``README`` 尚未被Git列入追蹤項目,可以使用 ``git add ...`` 或 ``git add .`` 將這些檔案列入追蹤,以列入後續提交變更 *(commit)* 的項目 58 | 59 | 由於這些檔案尚未被列入追蹤項目,因此可以使用 ``git add ...`` 將這些項目列入追蹤。 :: 60 | 61 | $ git add *.c 62 | $ git add README 63 | 64 | 若使用 ``git add .`` 則是直接將所有未加入追蹤的檔案一併列入追蹤。 65 | 66 | 將檔案列入追蹤之後,可以再使用 ``git status`` 察看目前的檔案狀態,可以觀察到檔案狀態已經變更了。 :: 67 | 68 | $ git status 69 | # On branch master 70 | # 71 | # Initial commit 72 | # 73 | # Changes to be committed: 74 | # (use "git rm --cached ..." to unstage) 75 | # 76 | # new file: HelloWorld.c 77 | # new file: README 78 | # 79 | 80 | 上方的 ``git status`` 結果,可以看到 *Changes to be committed* 指明了 ``HelloWorld.c`` 與 ``README`` 已經列入追蹤並且在等待提交變更之列。如果你後悔不想將這個項目列入追蹤,可以使用 ``git rm --cached ...`` 將指定的檔案移除追蹤項目。 81 | 82 | 最後,當你覺得這些檔案已經沒有變更需要,或是程式bug已經被修正之後,就可以使用 ``git commit`` 提交變更。 例如: :: 83 | 84 | $ git commit -m "The first commit of gittest project" 85 | [master (root-commit) 2ef8adc] The first commit of gittest project 86 | 0 files changed 87 | create mode 100644 HelloWorld.c 88 | create mode 100644 README 89 | 90 | 同樣地,上述的指令結果在以下分點說明: 91 | 92 | #. ``git commit -m <此次提交的簡短說明>`` , 若不使用 ``-m`` 參數,則會以系統偏好使用的編輯器請使用者輸入此次提交的簡短說明,如以下訊息(第1行是讓使用者輸入說明的地方): :: 93 | 94 | 1 # 95 | 2 # Please enter the commit message for your changes. Lines starting 96 | 3 # with '#' will be ignored, and an empty message aborts the commit. 97 | 4 # On branch master 98 | 5 # Changes to be committed: 99 | 6 # (use "git reset HEAD ..." to unstage) 100 | 101 | #. ``[master (root-commit) 2ef8adc]`` 代表於 ``master`` 分支上提交變更,提交變更碼為 ``2ef8adc`` (簡短碼) 102 | 103 | 每一次的提交變更,都會有一筆日誌紀錄,如果要察看這些日誌紀錄,可以使用 ``git log`` 察看。 :: 104 | 105 | $ git log 106 | commit 2ef8adccb61f46179f30ee017690519abb0b2c15 107 | Author: vans 108 | Date: Mon Jul 15 16:56:57 2013 +0800 109 | 110 | The first commit of gittest project 111 | 112 | 此外,如果沒有任何提交變更紀錄,就會出現類似以下的紀錄: :: 113 | 114 | fatal: bad default revision 'HEAD' 115 | 116 | *HEAD* 代表當前所在的分支(current branch),Git以 *HEAD* 做為指標以指明當前所在的分支。 117 | 118 | 提交變更之後,可以試試再看一次 ``git status`` ,就可以發現目前已經沒有任何檔案需要再提交變更。 :: 119 | 120 | $ git status 121 | # On branch master 122 | nothing to commit (working directory clean) 123 | 124 | 至此,我們可以用下圖闡述Git的檔案狀態的轉換。 125 | 126 | .. figure:: images/file_status.png 127 | :align: center 128 | 129 | Git的檔案狀態的轉換 [Ref2]_ 130 | 131 | .. [Ref2] Git Documentation, http://git-scm.com/documentation 132 | 133 | 134 | 圖上解釋了大部份的檔案狀態轉換,其中較為令人疑惑的狀態應為 *staged* 。事實上,在使用 ``git add ...`` 將檔案加入追蹤項目之後,在提交變更之前,就是處於 *staged* 的狀態,而以 ``git rm --cache ...`` 將檔案移出追蹤項目就會使得檔案轉換為 *unstaged* 狀態 *(not staged)* 。 135 | 136 | =========================== 137 | 單人開發模式 - 修改篇 138 | =========================== 139 | 140 | 進行首次的提交變更之後,可能會發現某些檔案又需要進行再次的修正,我們以修改 ``HelloWorld.c`` 做為練習,並以 ``git status`` 查看檔案狀態。 :: 141 | 142 | $ cat "#include " > HelloWorld.c 143 | $ git status 144 | # On branch master 145 | # Changes not staged for commit: 146 | # (use "git add ..." to update what will be committed) 147 | # (use "git checkout -- ..." to discard changes in working directory) 148 | # 149 | # modified: HelloWorld.c 150 | # 151 | no changes added to commit (use "git add" and/or "git commit -a") 152 | 153 | 從上述的修改結果,可以發現 ``HelloWorld.c`` 進入了 ``modified`` 的狀態,但仍處於 ``not staged`` 的狀態。 154 | 155 | 修改檔案後,我們可以有兩種選擇: 156 | 157 | #. ``git add ...`` 將檔案加入 ``stage`` 158 | #. ``git checkout -- ...`` 取消檔案變更,回到未更改前的狀態 159 | 160 | 此處,我們將檔案加入 ``stage`` ,以觀察檔案狀態變化。 :: 161 | 162 | $ git add HelloWorld.c 163 | $ git status 164 | # On branch master 165 | # Changes to be committed: 166 | # (use "git reset HEAD ..." to unstage) 167 | # 168 | # modified: HelloWorld.c 169 | # 170 | 171 | 上述結果可以發現, *Changes not staged for commit* 已變成 *Changes to be committed* ,代表檔案已經進入 ``staged`` 狀態。 172 | 173 | 此處可得出幾個小結論: 174 | 175 | #. 檔案修改後還沒被 ``git add`` 時的狀態為 ``unstaged`` 176 | #. 檔案修改後被 ``git add`` 之後的狀態為 ``staged`` 177 | #. ``staged`` 的檔案被提交變更之後就會回到 ``unmodified`` 178 | 179 | 同樣地,在提交變更之前,仍可使用 ``git reset HEAD ...`` 將檔案從 ``staged`` 狀態回復至 ``unstaged`` 。這些提示在使用 ``git status`` 時就能夠看到。接下來試著將檔案 ``unstage`` 。 :: 180 | 181 | $ git reset HEAD HelloWorld.c 182 | Unstaged changes after reset: 183 | M HelloWorld.c 184 | 185 | 同樣使用 ``git status`` 察看檔案狀態,可以發現檔案回到 ``unstaged`` 狀態,如下所示。 :: 186 | 187 | $ git status 188 | # On branch master 189 | # Changes not staged for commit: 190 | # (use "git add ..." to update what will be committed) 191 | # (use "git checkout -- ..." to discard changes in working directory) 192 | # 193 | # modified: HelloWorld.c 194 | # 195 | no changes added to commit (use "git add" and/or "git commit -a") 196 | 197 | 在 ``unstaged`` 的狀態下,還可以使用 ``git diff`` 指令,比較變動之前的版本與變動之後的差異。 :: 198 | 199 | $ git diff 200 | diff --git a/HelloWorld.c b/HelloWorld.c 201 | index e69de29..53c5fdf 100644 202 | --- a/HelloWorld.c 203 | +++ b/HelloWorld.c 204 | @@ -0,0 +1 @@ 205 | +#include 206 | 207 | 上述 ``git diff`` 的結果,左邊代表變動之前,右邊代表變動之後。 208 | 209 | 除了比較 ``unstaged`` 之檔案變動差異,也能夠指定Git比較與 ``staged`` 之差異。 :: 210 | 211 | $ git diff --staged 212 | 213 | ``git diff --staged`` 等於 ``git diff --cached`` 。原因在於使用 ``git add ...`` 之後,Git會將變動的檔案快取起來,因此 ``staged`` 又可以稱為 ``cached`` ,也由於快取機制,因此在檔案進入 ``staged`` 狀態之後,就存在一份快取,若此時再對檔案進行變動,其變動將不會出現,因為是以快取起來的檔案為主,所以進行 ``git diff`` 就無法看到差異。解決此種問題,就需要將檔案回復為 ``unstaged`` 狀態之後再變為 ``staged`` 。 214 | 215 | 瞭解 ``staged`` 與 ``unstaged`` 之後,就可以學會 ``git commit -a`` 偷懶指令。 :: 216 | 217 | $ git commit -a 218 | 219 | 上述指令代表將全部 ``unstaged`` 加到 ``staged`` 後直接提交變更。 220 | 221 | 本篇最後一個修改的指令為 ``git mv `` ,可使用此一指令對檔案進行重新命名。唯一需要注意的是此重新命名的功能並 **不區分大小寫** ,因此檔名相同但大小寫不同也會視為同一檔案而無法進行重新命名。 :: 222 | 223 | $ git mv README README_V2 224 | $ git status 225 | # On branch master 226 | # Changes to be committed: 227 | # (use "git reset HEAD ..." to unstage) 228 | # 229 | # renamed: README -> README_V2 230 | # 231 | 232 | =========================== 233 | 單人開發模式 - 刪除篇 234 | =========================== 235 | 236 | 開發過程也會遇到需要將檔案刪除的情況,若直接將檔案刪除,Git也能夠偵測到檔案已被刪除。但最好使用 ``git rm ...`` 較為安全,且能夠有回復的機會。 237 | 238 | 以下指令過程為新增一檔案 ``bad.c`` 並且提交之後以 ``git rm ...`` 刪除該檔案。 239 | 240 | #. 新增檔案 ``bad.c`` ,並提交變更 :: 241 | 242 | $ touch bad.c; git add .; git commit -a 243 | $ git log --graph 244 | * commit 19c3e6add40213002bcd46f2a5036f66ec5a0698 245 | | Author: vans 246 | | Date: Tue Jul 16 09:36:14 2013 +0800 247 | | 248 | | bad.c added 249 | | 250 | * commit 2ef8adccb61f46179f30ee017690519abb0b2c15 251 | Author: vans 252 | Date: Mon Jul 15 16:56:57 2013 +0800 253 | 254 | The first commit of gittest project 255 | $ 256 | $ ls 257 | HelloWorld.c README bad.c 258 | 259 | #. 刪除檔案 ``bad.c`` ,可以看到檔案 ``bad.c`` 已被刪除 :: 260 | 261 | $ git rm bad.c 262 | rm 'bad.c' 263 | $ 264 | $ ls 265 | HelloWorld.c README 266 | 267 | #. 使用 ``git status`` 可以發現檔案仍在 ``unstaged`` 仍可以回復 :: 268 | 269 | $ git status 270 | # On branch master 271 | # Changes not staged for commit: 272 | # (use "git add/rm ..." to update what will be committed) 273 | # (use "git checkout -- ..." to discard changes in working directory) 274 | # 275 | # deleted: bad.c 276 | # 277 | no changes added to commit (use "git add" and/or "git commit -a") 278 | 279 | #. 使用 ``git checkout -- ...`` 救回 ``bad.c`` :: 280 | 281 | $ git checkout bad.c 282 | $ ls 283 | HelloWorld.c README bad.c 284 | $ git status 285 | # On branch master 286 | nothing to commit (working directory clean) 287 | 288 | #. 再次刪除 ``bad.c`` :: 289 | 290 | $ git rm bad.c 291 | $ git commit -m "bad.c deleted" 292 | 293 | #. 察看日誌紀錄 :: 294 | 295 | $ git log 296 | commit a842389065ee6e28179de7b512fea9e9ddc33d41 297 | Author: vans 298 | Date: Tue Jul 16 09:42:02 2013 +0800 299 | 300 | bad.c deleted 301 | 302 | commit 19c3e6add40213002bcd46f2a5036f66ec5a0698 303 | Author: vans 304 | Date: Tue Jul 16 09:36:14 2013 +0800 305 | 306 | bad.c added 307 | 308 | commit 2ef8adccb61f46179f30ee017690519abb0b2c15 309 | Author: vans 310 | Date: Mon Jul 15 16:56:57 2013 +0800 311 | 312 | The first commit of gittest project 313 | 314 | #. 假設提交變更的說明寫錯,想要試圖更正的話,可以使用以下指令進行更正。 :: 315 | 316 | $ git commit --amend 317 | 318 | =================================== 319 | 單人開發模式 - 刪除Git日誌中的資料 320 | =================================== 321 | 322 | 開發程式、系統時,難免會有利用設定檔管理的方式,此時設定檔中不免會存放帳號密碼等隱密資訊,如果不小心將這個設定檔加到版本控制中,即使將設定檔刪除,也會存在日誌檔中。此時最好的辦法就是將日誌中的相關紀錄也一併刪除,刪除的方法可以參考以下方法( ``git filter-branch`` , ``bfg`` ): :: 323 | 324 | $ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch ' --prune-empty --tag-name-filter cat -- --all 325 | $ git push origin master --force 326 | 327 | 上述第2行指令將本地的日誌傳到遠端的儲存庫後,覆寫掉遠端儲存庫的日誌檔,確保不會有相關紀錄存在。 328 | 329 | 或者使用 ``bfg`` 指令: :: 330 | 331 | $ bfg --delete-file 332 | $ bfg --replace-text passwords.txt 333 | 334 | 詳情可以參照 `GitHub Remove sensitive data `_ 。 335 | 336 | ================= 337 | 多人合作開發模式 338 | ================= 339 | 340 | 多人合作開發模式中,會由Git Server提供中央的儲存庫(repository)儲存主線(master),再由其他團隊成員複製(clone)主線到各自的開發環境中成為分支(branch),在各自的環境內開發完成之後再將分支推送(push)回主線。如此重複循環作業。 341 | 342 | Git目前支援SSH, HTTP兩種協定進行網路協作。以SSH協定複製Git Server上專案的指令為 ``git clone `` ,例如: :: 343 | 344 | $ git clone git@10.2.0.15:project.git 345 | 346 | 複製完成之後,可以使用 ``git remote -v`` 或 ``git remote show origin`` 察看完整的遠端儲存庫資訊,例如: :: 347 | 348 | $ git remote -v 349 | origin git@10.2.0.15:~git/repositories/vans/GitRepo.git (fetch) 350 | origin git@10.2.0.15:~git/repositories/vans/GitRepo.git (push) 351 | $ 352 | $ git remote show origin 353 | * remote origin 354 | Fetch URL: git@10.2.0.15:~git/repositories/vans/GitRepo.git 355 | Push URL: git@10.2.0.15:~git/repositories/vans/GitRepo.git 356 | HEAD branch: master 357 | Remote branch: 358 | master tracked 359 | Local branch configured for 'git pull': 360 | master merges with remote master 361 | Local ref configured for 'git push': 362 | master pushes to master (fast-forwardable) 363 | 364 | 上述的結果中的 *fetch*, *push* 分別代表 *取得版本更新的位置* 與 *推送新版本的位置* 。 365 | 366 | 若要新增遠端儲存庫,可使用 ``git remote add `` ,例如: :: 367 | 368 | $ git remote add pb git://github.com/paulboone/ticgit.git 369 | $ git remote -v 370 | origin git://github.com/schacon/ticgit.git 371 | pb git://github.com/paulboone/ticgit.git 372 | 373 | 若是需要重新命名遠端儲存庫名稱,可使用 ``git remote rename `` 。 :: 374 | 375 | $ git remote rename pb my_pb 376 | 377 | 一般複製(clone)回來的程式版本,會以 ``git checkout -b `` 另外命名新的分支名稱,以取代原本的 ``master`` ,通常會以issue編號或是新功能名稱命名。例如: :: 378 | 379 | $ git checkout -b "HellowWorld" 380 | 381 | 建立新的分支之後,可以使用 ``git branch`` 查看目前分支,米字號(*)的項目代表目前所在分支。 :: 382 | 383 | $ git branch 384 | * HellowWorld 385 | master 386 | 387 | 如果要切換分支可以使用 ``git checkout `` 進行,例如::: 388 | 389 | $ git checkout master 390 | 391 | 刪除分支則是 ``git branch -d `` ,例如: :: 392 | 393 | $ git branch -d HellowWorld 394 | 395 | 順帶一提,變更本地(local)分支的指令為 ``git branch -m `` ,例如: :: 396 | 397 | $ git branch -m HellowWorld HelloWorld 398 | 399 | 如果你已經在想重新命名的分支內時,還可以簡化成 ``git branch -m `` ,例如: :: 400 | 401 | $ git branch -m HelloWorld 402 | 403 | 建立完分支之後,其實後續的作業模式就如同單人開發模式,唯一不同的是會在提交變更之後需要推送回遠端儲存庫,推送的指令為 ``git push [local_branch]`` ,例如: :: 404 | 405 | $ git push origin master 406 | 407 | 上述的指令為將本地的 ``master`` 推送回 ``origin`` 的遠端儲存庫。 408 | 409 | 有時候,推送的過程並不會毫無問題,由於是多人合作開發的情況,因此十分有可能在你推送新版本回遠端儲存庫時,其他團隊成員就已經有推送一版新的版本,導致你本地的版本並非最新的版本或是有版本衝突的問題。 410 | 411 | 在此假設成員A已經在一天前推送一版新的程式,並未通知你要進行版本更新,此時若你要進行推送新的版本,就有可能會得到類似以下的錯誤訊息。 :: 412 | 413 | $ git push origin master 414 | To git@10.2.0.15r:~git/repositories/vans/GitRepo.git 415 | ! [rejected] master -> master (non-fast-forward) 416 | error: failed to push some refs to 'git@10.2.0.15:~git/repositories/vans/GitRepo.git' 417 | hint: Updates were rejected because the tip of your current branch is behind 418 | hint: its remote counterpart. Merge the remote changes (e.g. 'git pull') 419 | hint: before pushing again. 420 | hint: See the 'Note about fast-forwards' in 'git push --help' for details. 421 | 422 | 上述的訊息明確地告知你版本並非是最新的,並且建議你使用 ``git pull`` 指令更新並合併最新的版本。但是我們並不建議使用 ``git pull`` 直接進行合併,在合併之前,我們希望你多下三個指令 **(git fetch, git diff, git merge)** 了解哪些部份被更動了,多一些關注總是好的! 423 | 424 | 以下就是以指令更新版本的過程。 :: 425 | 426 | $ git fetch origin master:tmp 427 | $ git diff tmp 428 | $ git merge tmp 429 | 430 | 上述的第1個指令為更新遠端儲存庫(origin)至本地並且建立一新分支 ``tmp`` ;第2個指令為比較與 ``tmp`` 分支的差異;第3個為合併 ``tmp`` 分支。 431 | 432 | 會希望大家使用 ``git pull`` 的原因在於 ``git pull`` 直接從遠端儲存庫更新並且直接合併到到當前的分支,在不了解變動的情況下,就很有可能搞砸你原本能夠運作的程式。而使用 ``git fetch`` 的不同點就是 ``git fetch`` 只抓取更新,並不會進行合併。 433 | 434 | 此外,上述3個指令也等同於以下3個指令。 :: 435 | 436 | $ git fetch origin master 437 | $ git log master origin/master 438 | $ git merge origin/master 439 | 440 | 上述第2個指令是比較本地 ``master`` 分支與遠端 ``origin/master`` 的差異,若加上 ``-p`` 則可以分頁顯示。( ``origin/master`` 是指遠端儲存庫的的 ``master`` 主線) 441 | 442 | 最後,在進行合併時,很有可能會遇到衝突的情況,通常由於檔案的同一行內容不同所造成。但是Git並無法為你決定哪一行要保留,因此就會跳出提示要你自行刪去不要的內容,保留你要的部份,例如: :: 443 | 444 | Auto-merging README 445 | CONFLICT (add/add): Merge conflict in README 446 | Automatic merge failed; fix conflicts and then commit the result. 447 | 448 | 上述的訊息告訴我們 ``README`` 檔案有衝突,因此我們必須修正這個衝突後再提交變更。此處就需要使用編輯器打開 ``README`` (或有衝突存在的檔案)。例如: :: 449 | 450 | $ vim README 451 | <<<<<<<<<< HEAD 452 | Our brahch 453 | ========== 454 | 455 | ========== 456 | Content of origin/master 457 | >>>>>>>>>> 458 | 459 | 只要是有衝突存在的區域,都會以上述的格式表示。 ``<<<<<<<<<< HEAD`` 到 ``==========`` 就是我們所在的分支的檔案內容;而 ``==========`` 到 ``>>>>>>>>>>`` 則是要合併的分支內容。修正的方法為保留需要的部份即可,修正完成之後再提交變更後,測試程式運作沒問題的話,就可以推送至遠端儲存庫了。 460 | 461 | 推送版本的過程中,可能也會有需要添加版本號的需求,因此可以為專案先貼上標籤。指令範例如下: :: 462 | 463 | $ git tag -a v0.1 -m 'my version 0.1' 464 | $ git tag 465 | v0.1 466 | 467 | 上述第1個指令為新增 *v0.1* 的標籤,並附上 *my version 0.1* 做為說明。第2個指令為顯示現有的標籤。 468 | 469 | 但是,推送版本時預設並不會分享標籤給其他成員,若要分享標籤則只要在 ``git push`` 加上 ``--tags`` 參數即可。 :: 470 | 471 | $ git push origin --tags 472 | 473 | 此外,若要顯示指定標籤的訊息: :: 474 | 475 | $ git show v0.1 476 | 477 | 478 | ====================================== 479 | 多人合作開發模式 - 設定 upstream 480 | ====================================== 481 | 482 | 除了上一章節提到的 `origin` 遠端儲存庫之外,有時候我們也會需要跟其他遠端儲存庫進行協作。最常見的就是 `fork` 別人的專案時,我們同時也希望可以把他們專案上面的程式碼變動,一併同步更新到我們本地端中,來保持我們本地端的程式是最新版的狀態。 483 | 484 | 我們把這種情況稱為設定 `upstream` 。 485 | 486 | 設定 `upstream` 其實就是增加一組遠端儲存庫的資訊而已。例如以下指令: :: 487 | 488 | $ git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git 489 | 490 | 就可以在 `git remote -v` 中看到增加一組名為 `upstream` 的設定。 :: 491 | 492 | $ git remote -v 493 | # origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch) 494 | # origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (push) 495 | # upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git (fetch) 496 | # upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPOSITORY.git (push) 497 | 498 | 接下來就可以用 `fetch`, `merge` 等指令來更新來自 `upstream` 的變動。 499 | 500 | 例如: :: 501 | 502 | $ git fetch upstream 503 | 504 | 或者: 505 | 506 | $ git merge upstream/master 507 | 508 | 以上就是 `upstream` 的用法。 509 | 510 | ================================================== 511 | 多人合作開發模式 - 下載遠端 Repository 的分支 512 | ================================================== 513 | 514 | 還有一種情況是我們想直接 clone 遠端 Repository 上的分支在本地端的分支上,可以使用以下指令: 515 | 516 | :: 517 | 518 | $ git checkout --track -b my-local-branch origin/ 519 | 520 | 如此一來,本地端就會新增一個與遠端分支一模一樣的新分支。 521 | 522 | ====================================== 523 | 多人合作開發模式 - 設定SSH金鑰認證 524 | ====================================== 525 | 526 | 由於透過SSH協定進行合作開發需要團隊成員在Git Server上有一組帳號,如此一來每一位成員在複製、推送或更新程式版本時都需要輸入帳號密碼。 527 | 528 | 若設定SSH金鑰認證,則同一團隊成員只要共用一組帳號即可,也可加強SSH帳號的安全性。 529 | 530 | 以下為SSH金鑰認證的設定過程,分為 *SSH Client端* 與 *SSH Server端* 。 531 | 532 | ------------------ 533 | SSH Client端 534 | ------------------ 535 | 536 | #. 產生一對金鑰(公鑰與私鑰) :: 537 | 538 | $ ssh-keygen -t rsa 539 | $ ssh-keygen -t dsa 540 | 541 | 上述兩種指令皆可。兩種指令會分別產生 (id_rsa, id_rsa.pub) 與 (id_dsa, id_dsa.pub) 的金鑰檔案。 542 | 543 | 注意: ``ssh-keygen`` 過程會詢問 *Enter passphrase (empty for no passphrase)* ,此處直接按Enter 跳過即可 544 | 545 | #. 把公鑰傳送到SSH Server(以 ``.pub`` 為副檔名的檔案為公鑰) 546 | 547 | #. 於家目錄下的 ``.ssh`` 資料夾內新增SSH Config,設定SSH以金鑰認證:: 548 | 549 | $ touch ~/.ssh/config 550 | 551 | #. 添加並修改以下內容至SSH Config檔內 :: 552 | 553 | $ vim ~/.ssh/config 554 | Host RemoteServer 555 | HostName 10.2.0.15 556 | Port 3333 557 | User git 558 | IdentityFile ~/.ssh/id_rsa 559 | 560 | ------------------ 561 | SSH Server端 562 | ------------------ 563 | 564 | #. 將client端傳送過來的公鑰命名成 authorized_keys,並且放到該團隊所共用帳號的家目錄下的 ``.ssh`` 資料夾內 :: 565 | 566 | cat id_rsa.pub >> ~git/.ssh/authorized_keys 567 | 568 | #. 確認 ``/etc/ssh/sshd_config`` 有開啟以下選項,並重新啟動SSH Server :: 569 | 570 | RSAAuthentication yes 571 | PubkeyAuthentication yes 572 | AuthorizedKeysFile %h/.ssh/authorized_keys 573 | 574 | ------------------------------------------ 575 | 測試Client端與Server端能否以金鑰認證 576 | ------------------------------------------ 577 | 578 | #. 測試SSH能不能免輸入密碼即可登入 :: 579 | 580 | $ ssh RemoteServer 581 | 582 | #. 為Git新增遠端儲存庫,並測試是否可以免輸入密碼推送 :: 583 | 584 | $ git remote add origin RemoteServer:path/to/repository.git 585 | $ git push origin 586 | -------------------------------------------------------------------------------- /source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # gitdoc documentation build configuration file, created by 4 | # sphinx-quickstart on Fri May 23 23:35:58 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | import sphinx_rtd_theme 24 | 25 | # -- General configuration ------------------------------------------------ 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | #needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | 'sphinx.ext.autodoc', 35 | 'sphinx.ext.doctest', 36 | 'sphinx.ext.intersphinx', 37 | 'sphinx.ext.todo', 38 | 'sphinx.ext.coverage', 39 | 'sphinx.ext.mathjax', 40 | 'sphinx.ext.ifconfig', 41 | 'sphinx.ext.viewcode', 42 | ] 43 | 44 | # Add any paths that contain templates here, relative to this directory. 45 | templates_path = ['_templates'] 46 | 47 | # The suffix of source filenames. 48 | source_suffix = '.rst' 49 | 50 | # The encoding of source files. 51 | #source_encoding = 'utf-8-sig' 52 | 53 | # The master toctree document. 54 | master_doc = 'index' 55 | 56 | # General information about the project. 57 | project = u'Practical guide for git users' 58 | copyright = u'2014, PwnDora' 59 | 60 | # The version info for the project you're documenting, acts as replacement for 61 | # |version| and |release|, also used in various other places throughout the 62 | # built documents. 63 | # 64 | # The short X.Y version. 65 | version = '0.1' 66 | # The full version, including alpha/beta/rc tags. 67 | release = '0.1' 68 | 69 | # The language for content autogenerated by Sphinx. Refer to documentation 70 | # for a list of supported languages. 71 | #language = None 72 | 73 | # There are two options for replacing |today|: either, you set today to some 74 | # non-false value, then it is used: 75 | #today = '' 76 | # Else, today_fmt is used as the format for a strftime call. 77 | #today_fmt = '%B %d, %Y' 78 | 79 | # List of patterns, relative to source directory, that match files and 80 | # directories to ignore when looking for source files. 81 | exclude_patterns = [] 82 | 83 | # The reST default role (used for this markup: `text`) to use for all 84 | # documents. 85 | #default_role = None 86 | 87 | # If true, '()' will be appended to :func: etc. cross-reference text. 88 | #add_function_parentheses = True 89 | 90 | # If true, the current module name will be prepended to all description 91 | # unit titles (such as .. function::). 92 | #add_module_names = True 93 | 94 | # If true, sectionauthor and moduleauthor directives will be shown in the 95 | # output. They are ignored by default. 96 | #show_authors = False 97 | 98 | # The name of the Pygments (syntax highlighting) style to use. 99 | pygments_style = 'sphinx' 100 | 101 | # A list of ignored prefixes for module index sorting. 102 | #modindex_common_prefix = [] 103 | 104 | # If true, keep warnings as "system message" paragraphs in the built documents. 105 | #keep_warnings = False 106 | 107 | 108 | # -- Options for HTML output ---------------------------------------------- 109 | 110 | # The theme to use for HTML and HTML Help pages. See the documentation for 111 | # a list of builtin themes. 112 | # html_theme = 'default' 113 | html_theme = "sphinx_rtd_theme" 114 | 115 | # Theme options are theme-specific and customize the look and feel of a theme 116 | # further. For a list of options available for each theme, see the 117 | # documentation. 118 | #html_theme_options = {} 119 | 120 | # Add any paths that contain custom themes here, relative to this directory. 121 | #html_theme_path = [] 122 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 123 | 124 | # The name for this set of Sphinx documents. If None, it defaults to 125 | # " v documentation". 126 | #html_title = None 127 | 128 | # A shorter title for the navigation bar. Default is the same as html_title. 129 | #html_short_title = None 130 | 131 | # The name of an image file (relative to this directory) to place at the top 132 | # of the sidebar. 133 | #html_logo = None 134 | 135 | # The name of an image file (within the static path) to use as favicon of the 136 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 137 | # pixels large. 138 | #html_favicon = None 139 | 140 | # Add any paths that contain custom static files (such as style sheets) here, 141 | # relative to this directory. They are copied after the builtin static files, 142 | # so a file named "default.css" will overwrite the builtin "default.css". 143 | html_static_path = ['_static'] 144 | 145 | # Add any extra paths that contain custom files (such as robots.txt or 146 | # .htaccess) here, relative to this directory. These files are copied 147 | # directly to the root of the documentation. 148 | #html_extra_path = [] 149 | 150 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 151 | # using the given strftime format. 152 | #html_last_updated_fmt = '%b %d, %Y' 153 | 154 | # If true, SmartyPants will be used to convert quotes and dashes to 155 | # typographically correct entities. 156 | #html_use_smartypants = True 157 | 158 | # Custom sidebar templates, maps document names to template names. 159 | #html_sidebars = {} 160 | 161 | # Additional templates that should be rendered to pages, maps page names to 162 | # template names. 163 | #html_additional_pages = {} 164 | 165 | # If false, no module index is generated. 166 | #html_domain_indices = True 167 | 168 | # If false, no index is generated. 169 | #html_use_index = True 170 | 171 | # If true, the index is split into individual pages for each letter. 172 | #html_split_index = False 173 | 174 | # If true, links to the reST sources are added to the pages. 175 | #html_show_sourcelink = True 176 | 177 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 178 | #html_show_sphinx = True 179 | 180 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 181 | #html_show_copyright = True 182 | 183 | # If true, an OpenSearch description file will be output, and all pages will 184 | # contain a tag referring to it. The value of this option must be the 185 | # base URL from which the finished HTML is served. 186 | #html_use_opensearch = '' 187 | 188 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 189 | #html_file_suffix = None 190 | 191 | # Output file base name for HTML help builder. 192 | htmlhelp_basename = 'gitdoc' 193 | 194 | 195 | # -- Options for LaTeX output --------------------------------------------- 196 | 197 | latex_elements = { 198 | # The paper size ('letterpaper' or 'a4paper'). 199 | #'papersize': 'letterpaper', 200 | 201 | # The font size ('10pt', '11pt' or '12pt'). 202 | #'pointsize': '10pt', 203 | 204 | # Additional stuff for the LaTeX preamble. 205 | #'preamble': '', 206 | } 207 | 208 | # Grouping the document tree into LaTeX files. List of tuples 209 | # (source start file, target name, title, 210 | # author, documentclass [howto, manual, or own class]). 211 | latex_documents = [ 212 | ('index', 'gitdoc.tex', u'gitdoc Documentation', 213 | u'PwnDora', 'manual'), 214 | ] 215 | 216 | # The name of an image file (relative to this directory) to place at the top of 217 | # the title page. 218 | #latex_logo = None 219 | 220 | # For "manual" documents, if this is true, then toplevel headings are parts, 221 | # not chapters. 222 | #latex_use_parts = False 223 | 224 | # If true, show page references after internal links. 225 | #latex_show_pagerefs = False 226 | 227 | # If true, show URL addresses after external links. 228 | #latex_show_urls = False 229 | 230 | # Documents to append as an appendix to all manuals. 231 | #latex_appendices = [] 232 | 233 | # If false, no module index is generated. 234 | #latex_domain_indices = True 235 | 236 | 237 | # -- Options for manual page output --------------------------------------- 238 | 239 | # One entry per manual page. List of tuples 240 | # (source start file, name, description, authors, manual section). 241 | man_pages = [ 242 | ('index', 'gitdoc', u'gitdoc Documentation', 243 | [u'PwnDora'], 1) 244 | ] 245 | 246 | # If true, show URL addresses after external links. 247 | #man_show_urls = False 248 | 249 | 250 | # -- Options for Texinfo output ------------------------------------------- 251 | 252 | # Grouping the document tree into Texinfo files. List of tuples 253 | # (source start file, target name, title, author, 254 | # dir menu entry, description, category) 255 | texinfo_documents = [ 256 | ('index', 'gitdoc', u'gitdoc Documentation', 257 | u'PwnDora', 'gitdoc', 'One line description of project.', 258 | 'Miscellaneous'), 259 | ] 260 | 261 | # Documents to append as an appendix to all manuals. 262 | #texinfo_appendices = [] 263 | 264 | # If false, no module index is generated. 265 | #texinfo_domain_indices = True 266 | 267 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 268 | #texinfo_show_urls = 'footnote' 269 | 270 | # If true, do not generate a @detailmenu in the "Top" node's menu. 271 | #texinfo_no_detailmenu = False 272 | 273 | 274 | # -- Options for Epub output ---------------------------------------------- 275 | 276 | # Bibliographic Dublin Core info. 277 | epub_title = u'gitdoc' 278 | epub_author = u'PwnDora' 279 | epub_publisher = u'PwnDora' 280 | epub_copyright = u'2014, PwnDora' 281 | 282 | # The basename for the epub file. It defaults to the project name. 283 | #epub_basename = u'gitdoc' 284 | 285 | # The HTML theme for the epub output. Since the default themes are not optimized 286 | # for small screen space, using the same theme for HTML and epub output is 287 | # usually not wise. This defaults to 'epub', a theme designed to save visual 288 | # space. 289 | #epub_theme = 'epub' 290 | 291 | # The language of the text. It defaults to the language option 292 | # or en if the language is not set. 293 | #epub_language = '' 294 | 295 | # The scheme of the identifier. Typical schemes are ISBN or URL. 296 | #epub_scheme = '' 297 | 298 | # The unique identifier of the text. This can be a ISBN number 299 | # or the project homepage. 300 | #epub_identifier = '' 301 | 302 | # A unique identification for the text. 303 | #epub_uid = '' 304 | 305 | # A tuple containing the cover image and cover page html template filenames. 306 | #epub_cover = () 307 | 308 | # A sequence of (type, uri, title) tuples for the guide element of content.opf. 309 | #epub_guide = () 310 | 311 | # HTML files that should be inserted before the pages created by sphinx. 312 | # The format is a list of tuples containing the path and title. 313 | #epub_pre_files = [] 314 | 315 | # HTML files shat should be inserted after the pages created by sphinx. 316 | # The format is a list of tuples containing the path and title. 317 | #epub_post_files = [] 318 | 319 | # A list of files that should not be packed into the epub file. 320 | epub_exclude_files = ['search.html'] 321 | 322 | # The depth of the table of contents in toc.ncx. 323 | #epub_tocdepth = 3 324 | 325 | # Allow duplicate toc entries. 326 | #epub_tocdup = True 327 | 328 | # Choose between 'default' and 'includehidden'. 329 | #epub_tocscope = 'default' 330 | 331 | # Fix unsupported image types using the PIL. 332 | #epub_fix_images = False 333 | 334 | # Scale large images. 335 | #epub_max_image_width = 0 336 | 337 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 338 | #epub_show_urls = 'inline' 339 | 340 | # If false, no index is generated. 341 | #epub_use_index = True 342 | 343 | 344 | # Example configuration for intersphinx: refer to the Python standard library. 345 | intersphinx_mapping = {'http://docs.python.org/': None} 346 | -------------------------------------------------------------------------------- /source/configurations.rst: -------------------------------------------------------------------------------- 1 | Git基本設定 2 | =========== 3 | 4 | ======================== 5 | First thing first. 6 | ======================== 7 | 8 | 利用Git進行版本控制之前,需要設定 ``user.name`` 及 ``user.email`` 。主要原因在於能夠透過這兩個設定了解哪些更動是由哪位成員所做出的,並且提供email的聯絡方式,使得團隊成員彼此間能夠確保至少有一個聯絡的管道。 9 | 10 | 設定 ``user.name`` 及 ``user.email`` 的指令如下所示︰ :: 11 | 12 | $ git config --global user.name "Your Name" 13 | $ git config --global user.email you@example.com 14 | 15 | #. 上述指令加上 ``--global`` 代表全域設定 16 | #. 每一位成員送出新版本(push)時,就是依照上述兩個設定紀錄於Git的日誌檔中 17 | 18 | 設定 ``user.name`` 及 ``user.email`` 之後,可以使用 ``git config --list`` 查看設定確認是否有設定成功。 :: 19 | 20 | $ git config --list 21 | 22 | 上述指令顯示結果如下︰ :: 23 | 24 | user.email=vans@gmail.com 25 | user.name=vans 26 | core.repositoryformatversion=0 27 | core.filemode=true 28 | core.bare=false 29 | core.logallrefupdates=true 30 | core.ignorecase=true 31 | core.precomposeunicode=false 32 | 33 | 而上述的設定的內容,其實是存在使用者的家目錄下的 ``.gitconfig`` 檔案內( ``~/.gitconfig`` ),因此直接更動這個檔案也可以更改設定。 34 | 35 | =============================== 36 | 設定預設的推送(Push)模式 37 | =============================== 38 | 39 | 從Git推送(Push)模式目前有 ``nothing`` , ``matching`` , ``upstream`` , ``simple`` , ``current`` ,共5種。如果不指定的話,就可能就會顯示以下類似的訊息(依Git版本而異)。 :: 40 | 41 | warning: push.default is unset; its implicit value is changing in 42 | Git 2.0 from 'matching' to 'simple'. To squelch this message 43 | and maintain the current behavior after the default changes, use: 44 | 45 | git config --global push.default matching 46 | 47 | To squelch this message and adopt the new behavior now, use: 48 | 49 | git config --global push.default simple 50 | 51 | See 'git help config' and search for 'push.default' for further information. 52 | (the 'simple' mode was introduced in Git 1.7.11. Use the similar mode 53 | 'current' instead of 'simple' if you sometimes use older versions of Git) 54 | 55 | 那麼要選擇哪一種Push模式? 56 | 各位可以依照需求設定,詳細的各種模式說明可以使用以下指令查詢 ``push.default`` 的部份: :: 57 | 58 | $ git help config 59 | 60 | 但一般建議各位使用 ``current`` 或 ``simple`` 即可,使用 ``current`` 模式代表只將目前所在的分支推送至程式碼儲存庫(repository)。相較於其他的推送模式而言,較為安全與單純,設定使用 ``current`` 推送模式可以使用以下指令: :: 61 | 62 | $ git config --global push.default current 63 | 64 | ======================= 65 | 設定不需版本控制的檔案 66 | ======================= 67 | 68 | 進行系統程式開發時,不免會遇到一些檔案需要針對自己的系統環境進行設定,例如設定資料庫帳號密碼,或是使用 ``vim`` 可能會留下 ``.swp`` 暫存檔,這些可能具敏感機密的檔案如果不在版本控制系統中設定忽略的規則,就可能會造成資安事件。 69 | 70 | 因此,團隊成員之間必須在專案開始前,討論並設定必須將哪些檔案排除在版本控制之外。Git版本控制系統則提供 ``gitignore`` 的設定,可以將指定的檔案排除版本控制的範圍。 71 | 72 | 有2種方式設定 ``gitignore`` ,一是於專案資料夾內新增 ``.gitignore`` ,二是於使用者家目錄下新增 ``.gitignore`` ,建議2種方式都使用,原因在於讓被包含在專案的 ``.gitignore`` 只需設定專案內部版本控制需要忽略的檔案,使得其他團隊成員就算忘記建立 ``.gitignore`` 也能夠保有一定程度的安全性;而使用者家目錄下的 ``.gitignore`` 則是可依照各人開發習慣以及作業系統的不同進行設定,而不會影響團隊其他成員的 ``.gitignore`` 。 73 | 74 | 以下是在使用者家目錄下新增MAC OS X ``.gitignore`` 的指令及 ``.gitignore`` 檔案內容 [#f1]_ 。 75 | 76 | :: 77 | 78 | $ cd ~ 79 | $ git config --global core.excludesfile ~/.gitignore #設定讀取使用者家目錄下的.gitignore 80 | $ touch .gitignore 81 | $ vim .gitignore 82 | 83 | MAC OS X 可適用的 ``.gitignore`` 檔案內容 :: 84 | 85 | # Compiled source 86 | *.com 87 | *.class 88 | *.dll 89 | *.exe 90 | *.o 91 | *.so 92 | 93 | # Packages 94 | # it's better to unpack these files and commit the raw source 95 | # git has its own built in compression methods 96 | *.7z 97 | *.dmg 98 | *.gz 99 | *.iso 100 | *.jar 101 | *.rar 102 | *.tar 103 | *.zip 104 | 105 | # Logs and databases 106 | *.log 107 | *.sql 108 | *.sqlite 109 | 110 | # OS generated files 111 | .DS_Store 112 | .DS_Store? 113 | ._* 114 | .Spotlight-V100 115 | .Trashes 116 | ehthumbs.db 117 | Thumbs.db 118 | 119 | # Editor generated files 120 | *.swp 121 | *~ 122 | 123 | #. ``.gitignore`` 內的設定支援 ``glob`` 語法(簡化過的正規表示式) 124 | #. ``.gitignore`` 也可設定子目錄下的忽略規則,如 ``output/*.*`` 125 | 126 | ================================== 127 | 設定Git預設所使用的diff演算法 128 | ================================== 129 | 130 | Git也使用了多種不同的diff演算法,用來進行不同版本間的程式碼或文字內容的比對,目前有 ``patience`` , ``minimal`` , ``histogram`` , ``myers`` 4種diff演算法,預設使用 ``myers`` 演算法進行比對,此種演算法是使用貪婪(greedy)的方式進行比對,因此有時候比對結果會與我們預期的結果有些出入,建議可以使用 ``patience`` 演算法做為預設的diff演算法。 131 | 132 | 設定預設diff演算法之指令為: :: 133 | 134 | $ git config --global diff.algorithm patience 135 | 136 | =================================== 137 | 設定好用的Git指令縮寫 138 | =================================== 139 | 140 | 有些常用的Git指令可以利用別名(alias)的功能變成縮寫,可以有效增加工作效率,以下是一些常用的指令別名的設定: :: 141 | 142 | $ git config --global alias.co checkout 143 | $ git config --global alias.ci commit 144 | $ git config --global alias.st status 145 | $ git config --global alias.br branch 146 | 147 | 此外,有很多網路資源也各自整理了不少常用的指令縮寫,也建議各位可以針對需求增加。 148 | 以下列出一些相對重要的指令別名設定: 149 | 150 | * 設定檔案層級的更改忽略(將以下設定寫入至 ``.gitconfig`` 的 ``[alias]`` 區塊內) 151 | 152 | :: 153 | 154 | [alias] 155 | assume = update-index --assume-unchanged 156 | unassume = update-index --no-assume-unchanged 157 | assumed = "!git ls-files -v | grep ^h | cut -c 3-" 158 | unassumeall = "!git assumed | xargs git update-index --no-assume-unchanged" 159 | assumeall = "!git st -s | awk {'print $2'} | xargs git assume" 160 | 161 | 上述設定的簡要說明如下: 162 | 163 | ``.gitignore`` 設定檔的功能在於將檔案排除在版本控制的範疇之外,但是有些應用程式的開發不免需要設定檔,設定檔就不太適合列入 ``.gitignore`` 中,因為我們也需要將設定檔的預設項目列入到版本控制之中,但是每個團隊成員經常在開發過程也會針對各自的任務不同而變更設定檔,如果這些變更也被一併提交推送到程式儲存庫中,就可能變更了預設的設定檔的狀態,也可能造成其他成員在合併設定檔時的衝突。 164 | 165 | 針對這個問題,我們可以使用 ``git update-index`` 來變更設定檔的狀態,這也是我們設定 ``assume`` , ``unassume`` 等別名的原因。 166 | 167 | 例如將設定檔的狀態從「已更改(modified)」變更為「未更改」: 168 | 169 | :: 170 | 171 | $ git update --assume-unchanged your_config 172 | $ git assume your_config #此指令與上一指令相同 173 | 174 | * 設定快照(snapshot)功能(將以下設定寫入至 ``.gitconfig`` 的 ``[alias]`` 區塊內) 175 | 176 | :: 177 | 178 | [alias] 179 | snapshot = !git stash save "snapshot: $(date)" && git stash apply "stash@{0}" 180 | 181 | 這個快照功能實際上是使用 ``git stash`` 功能來達成的。簡單而言,就是將目前分支的所有變更以時間為名稱存一份起來,如此一來,就等於是達成了快照功能,你就能夠使用 ``git stash apply "快照名稱"`` 來回復到你所想要回到的那個時間點。 182 | 183 | 可以使用以下指令進行快照、列出所有快照、回復快照: :: 184 | 185 | $ git snapshot #快照 186 | $ git stash list #列出快照 187 | stash@{0}: On master: Thu Feb 13 14:28:38 CST 2014 188 | stash@{1}: On master: Thu Feb 12 11:20:58 CST 2014 189 | $ git stash apply stash@{1} #回復到2014/2/12所做的快照 190 | 191 | 以上就是2個相對重要的功能,如果不懂的話,先有個認識即可,本文會在後續章節中再做一次介紹。 192 | 193 | ==== 194 | 其他 195 | ==== 196 | 197 | * 設定偏好的文字編輯器 :: 198 | 199 | $ git config --global core.editor "vim" 200 | 201 | * 開啟Git文字輸出以彩色形式輸出(Git預設輸出是沒有顏色的,我們可以讓Git在輸出時加上顏色讓我們更容易閱讀) :: 202 | 203 | $ git config --global color.ui true 204 | $ git config --global color.status always 205 | 206 | * 客製化提交變更說明 :: 207 | 208 | $ vim your_project/.git/COMMIT_EDITMSG 209 | 210 | # 建議格式 211 | 212 | subject line 213 | 214 | what happened 215 | 216 | [ticket: X] 217 | # Please enter the commit message for your changes. Lines starting 218 | # with '#' will be ignored, and an empty message aborts the commit. 219 | # On branch master 220 | # Changes to be committed: 221 | # (use "git reset HEAD ..." to unstage) 222 | # 223 | # modified: lib/test.rb 224 | # 225 | 226 | * 更多其他設定可以參閱參考資料 [#f2]_ 227 | 228 | 229 | .. rubric:: Footnotes 230 | 231 | .. [#f1] `Ignoring files `_ , https://help.github.com/articles/ignoring-files 232 | 233 | .. [#f2] `Customizing Git - Git Configuration `_ , http://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration 234 | -------------------------------------------------------------------------------- /source/conflict.rst: -------------------------------------------------------------------------------- 1 | 實戰版本衝突 (Dealing with Conflict) 2 | ========================================== 3 | 4 | 多人的開發團隊在實作各種不同的功能時,版本衝突是肯定會遇到的一種情況,除了慢慢修正一個接一個 Conflict 的選擇之外,以下陳述在幾種情況下可以省時費力的解決方法。 5 | 6 | ============================ 7 | --theirs, --ours 8 | ============================ 9 | 10 | 在十分確定衝突的版本必須以對方的版本為準時,可以使用以下指令: :: 11 | 12 | $ git checkout --theirs 13 | 14 | 上述的指令顧名思義就是 **把有衝突的檔案還原到對方的版本** 。 15 | 16 | 反之,如果要以我們自己的版本為準時,就可以改用 ``--ours`` : :: 17 | 18 | $ git checkout --ours 19 | 20 | =================================== 21 | 以遠端分支為準,解決版本衝突 22 | =================================== 23 | 24 | 此外,在 pull remote branch 遇到版本衝突時,如果在十分確定以遠端分支為準時,可以使用以下指令: :: 25 | 26 | $ git checkout origin/master 27 | 28 | 上述的指令就是將本地有衝突的檔案,還原到遠端 ``origin/master`` 分支的版本。 29 | -------------------------------------------------------------------------------- /source/gitlab.rst: -------------------------------------------------------------------------------- 1 | GitLab介紹 2 | ========== 3 | 4 | 目前最流行的線上Git專案管理系統可以說是非 `GitHub`_ 莫屬,對於一般OpenSource的專案選擇使用GitHub做為線上Git專案管理系統即可,也免收任何費用。但對於組織內部所開發的非OpenSource的專案而言,選擇使用GitHub不僅需要付費,保密性也有相當的顧慮。 5 | 6 | 若組織亦有上述的顧慮,可以選擇替代方案-`GitLab`_ ! 7 | 8 | .. _GitHub: https://github.com/ 9 | 10 | .. _GitLab: http://gitlab.org/ 11 | 12 | ========================================= 13 | Self hosted Git management software 14 | ========================================= 15 | 16 | 事實上,若形容GitLab就是能夠hosting在組織內部網路的GitHub一點也不為過,因為其介面、功能都與GitHub十分神似,有使用GitHub經驗的開發者也能夠很快速上手。 17 | 18 | .. figure:: images/GitLab/gitlab_project_tree.png 19 | :align: center 20 | :width: 700px 21 | 22 | GitLab介面 23 | 24 | 而目前GitLab已整合了以下功能: 25 | 26 | #. Repository access 27 | #. Administration 28 | #. Issues 29 | #. Forks 30 | #. Code review 31 | #. Wiki 32 | #. Merge Requests 33 | #. Web Editor 34 | 35 | 如果有興趣的人也可以試試線上Demo版, `線上Demo連結`_ 。 36 | 37 | .. _線上Demo連結: http://demo.gitlab.com/users/sign_in 38 | -------------------------------------------------------------------------------- /source/gitlabworkflow.rst: -------------------------------------------------------------------------------- 1 | GitLab工作流程 2 | ============== 3 | 4 | GitLab的工作流程其實與前述所提到的多人合作開發模式相差無幾。 5 | 6 | 以下是直接引述GitLab所提供之 GitLab Workflow : 7 | 8 | #. Clone project :: 9 | 10 | $ git clone git@example.com:project-name.git 11 | 12 | #. Create branch with your feature :: 13 | 14 | $ git checkout -b $feature_name 15 | 16 | #. Write code. Commit changes :: 17 | 18 | $ git commit -am "My feature is ready" 19 | 20 | #. Push your branch to GitLab :: 21 | 22 | $ git push origin $feature_name 23 | 24 | #. Review your code on Commits page 25 | #. Create a merge request 26 | #. Your team lead will review code & merge it to main branch 27 | 28 | 事實上,就這麼簡單。 29 | 30 | 以下主要補充使用GitLab需要注意的地方。 31 | 32 | --------------------------- 33 | 建立新專案 34 | --------------------------- 35 | 36 | #. 介面操作1,新增專案 37 | 38 | .. figure:: images/GitLab/create_project1.png 39 | :width: 600px 40 | :align: center 41 | 42 | #. 介面操作2,輸入專案名稱 43 | 44 | .. figure:: images/GitLab/create_project2.png 45 | :width: 600px 46 | :align: center 47 | 48 | #. 介面操作3,新增完成(紅框部份需修改) 49 | 50 | .. figure:: images/GitLab/create_project3.png 51 | :width: 600px 52 | :align: center 53 | 54 | 完成上述3個步驟之後,其實只是在資料庫中建立專案的資料而已,並非在GitLab的檔案系統內建立一個新的專案資料夾,因此GitLab也分別針對全新專案及既有專案兩種情況,提供了一些指令說明如何建立專案資料夾。 55 | 56 | * 建立新儲存庫 :: 57 | 58 | mkdir myfirstproject 59 | cd myfirstproject 60 | git init 61 | touch README 62 | git add README 63 | git commit -m 'first commit' 64 | git remote add origin git@10.2.0.15:your_account/myfirstproject.git 65 | git push -u origin master 66 | 67 | * 匯入既有專案 :: 68 | 69 | cd existing_git_repo 70 | git remote add origin git@10.2.0.15:your_account/myfirstproject.git 71 | git push -u origin master 72 | 73 | 上述指令若GitLab安裝過程無誤,是可以良好運作的,如有任何問題,大多是網路環境(GitLab Server裝於VM內或使用NAT等原因),這些情況就得更改上述指令才能運作。 74 | 75 | ---------------- 76 | 匯入公鑰 77 | ---------------- 78 | 79 | #. 介面操作1 80 | 81 | .. figure:: images/GitLab/import_pubkey1.png 82 | :align: center 83 | :width: 600px 84 | 85 | #. 介面操作2 86 | 87 | .. figure:: images/GitLab/import_pubkey2.png 88 | :align: center 89 | :width: 600px 90 | 91 | ---------------- 92 | 開放專案 93 | ---------------- 94 | 95 | 預設的專案是不開放的(private),所以是無法被其他人(除了Team, Group成員之外)知道這個專案的存在。如果想開放專案給所有人看到,請參照以下操作。 96 | 97 | .. figure:: images/GitLab/public_access.png 98 | :align: center 99 | :width: 600px 100 | -------------------------------------------------------------------------------- /source/images/GitLab/create_project1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/GitLab/create_project1.png -------------------------------------------------------------------------------- /source/images/GitLab/create_project2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/GitLab/create_project2.png -------------------------------------------------------------------------------- /source/images/GitLab/create_project3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/GitLab/create_project3.png -------------------------------------------------------------------------------- /source/images/GitLab/gitlab_project_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/GitLab/gitlab_project_tree.png -------------------------------------------------------------------------------- /source/images/GitLab/import_pubkey1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/GitLab/import_pubkey1.png -------------------------------------------------------------------------------- /source/images/GitLab/import_pubkey2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/GitLab/import_pubkey2.png -------------------------------------------------------------------------------- /source/images/GitLab/public_access.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/GitLab/public_access.png -------------------------------------------------------------------------------- /source/images/after_rebase_onto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/after_rebase_onto.png -------------------------------------------------------------------------------- /source/images/before_rebase_onto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/before_rebase_onto.png -------------------------------------------------------------------------------- /source/images/file_status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/file_status.png -------------------------------------------------------------------------------- /source/images/git-branching-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/git-branching-model.png -------------------------------------------------------------------------------- /source/images/master_branch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/master_branch.png -------------------------------------------------------------------------------- /source/images/merge-without-ff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/merge-without-ff.png -------------------------------------------------------------------------------- /source/images/merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/merge.png -------------------------------------------------------------------------------- /source/images/rebase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/rebase.png -------------------------------------------------------------------------------- /source/images/rebase_feature_a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/rebase_feature_a.png -------------------------------------------------------------------------------- /source/images/rebase_feature_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spitfire-sidra/git-tutorial/25160182713b8a8154e3d80d11c0b45e9e93cc2c/source/images/rebase_feature_b.png -------------------------------------------------------------------------------- /source/index.rst: -------------------------------------------------------------------------------- 1 | .. GitWorkflows documentation master file, created by 2 | sphinx-quickstart on Tue Jul 23 15:21:26 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Practical guide for git users 7 | ============================================= 8 | 9 | 本文件旨於教導使用者各種操作 Git 及 GitLab 的實務技巧,從基礎指令到分支策略皆有詳細介紹。希望透過此文件學習,能提高團隊協作開發之能力。 10 | 11 | Contents: 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | installation 17 | configurations 18 | commands 19 | branchingmodel 20 | conflict 21 | rebase 22 | tagging 23 | gitlab 24 | gitlabworkflow 25 | other 26 | references 27 | -------------------------------------------------------------------------------- /source/installation.rst: -------------------------------------------------------------------------------- 1 | 安裝說明 2 | ========= 3 | 4 | 由於目前Git, GitLab 已經是十分成熟的工具與系統,因此相關安裝說明請自行參閱以下連結。 5 | 6 | * `Git 官方網站 `_ 7 | * `GitLab 官方網站 `_ 8 | 9 | 此外,GitLab官方目前僅於Debian及Ubuntu系統環境內進行開發測試,因此推薦使用Debian或Ubuntu安裝GitLab。 10 | -------------------------------------------------------------------------------- /source/other.rst: -------------------------------------------------------------------------------- 1 | 其他 2 | ============== 3 | 4 | 5 | ======================== 6 | git archive 打包程式 7 | ======================== 8 | 9 | 有時候我們會需要把整個程式碼打包成一個壓縮檔,同時把裡面的 ``.git`` 資料夾所儲存的版本控制歷程給移除,可以用以下指令進行打包: :: 10 | 11 | $ git archive --format= --output= 12 | 13 | 例如: :: 14 | 15 | $ git archive master --format=zip --output=./app_v1.20150621.zip 16 | 17 | $ git archive master --format=tar --output=./app_v1.20150621.tar 18 | 19 | 此外,還有把當前分支最新的版本打包更快方式: :: 20 | 21 | $ git archive -o latest.zip HEAD 22 | 23 | 24 | ======================= 25 | git 產生 patch 檔 26 | ======================= 27 | 28 | 雖然在版本控制環境下,應該會很少會需要用到 patch 的功能,但有時候會需要把版本間的差異記錄下來,作為回復之用或者給上述移除版本控制歷程的程式緊急更新之用,就會需要用到 patch 的功能。 29 | 30 | 最簡單的 patch 功能只要利用 git diff 即可產生 patch 檔: :: 31 | 32 | $ git diff ... > diff.patch 33 | 34 | 例如跟遠端的 master 比較差異後產生 patch 檔: :: 35 | 36 | $ git diff origin/master...HEAD > diff.patch 37 | 38 | 例如產生當前 unstaged 的 patch 檔: :: 39 | 40 | $ git diff > diff.patch 41 | 42 | 例如產生 staged 的 patch 檔: :: 43 | 44 | $ git diff --staged > diff.patch 45 | 46 | 套用 patch 檔指令或者可使用 ``patch`` 指令: :: 47 | 48 | $ git apply diff.patch 49 | or 50 | $ patch -p0 < diff.patch # 回復可以用 patch -R -p0 < diff.patch 51 | 52 | 套用 patch 檔之後,仍需要進行 add, commit 等動作, git 並不會自動幫我們 commit 。 53 | 54 | 55 | 第 2 種產生 patch 檔的功能是用 ``git format-patch`` ,只要指定從哪個 commit id 之後產生 patch 即可,執行之後就會按照順序將一個個 commit 之間的差異產生 patch 檔,不過這種方式所產生的 patch 檔只有 git 能夠使用,必須注意: :: 56 | 57 | $ git format-patch 58 | 59 | 例如產生從 commit id **13e50ace92** 開始產生 patch 檔 : :: 60 | 61 | $ git format-patch 13e50ace92 62 | 0001-added-conflict.rst.patch 63 | 0002-fixed-syntax.patch 64 | 0003-added-a-new-chapter.patch 65 | 66 | 執行結果就是按照次序產生一個個 patch 檔。 67 | 68 | 如果只是需要該 commit id 之後的特定幾個 patch 檔,則可以加上數字來限制: :: 69 | 70 | $ git format-patch 13e50ace92 -1 71 | 0001-added-conflict.rst.patch 72 | 73 | 而 2 個 commit id 之間的 patch 檔則可以用: :: 74 | 75 | $ git format-patch 76 | 77 | 套用 patch 檔則是使用 ``git am`` : :: 78 | 79 | 套用所有 patch 檔: :: 80 | 81 | $ git am *.patch 82 | 83 | 套用單個 patch 檔: :: 84 | 85 | $ git am 0001-added-conflict.rst.patch 86 | 87 | 如果套用 patch 時有遇到 conflict 或是用以下幾個指令來解決: 88 | 89 | 解決 conflict 後 :: 90 | 91 | $ git am --continue 92 | 93 | 或者不想解決 conflict 想直接跳過這個 patch : :: 94 | 95 | $ git am --skip 96 | 97 | 又或者想乾脆放棄,直接回復原本的狀態: :: 98 | 99 | $ git am --abort 100 | -------------------------------------------------------------------------------- /source/rebase.rst: -------------------------------------------------------------------------------- 1 | Rebase 2 | ====================== 3 | 4 | 常言道「不會 `rebase` ,就等於沒學過 Git」,由此可見 `rebase` 在 Git 內有多重要。 5 | 6 | 在開始本文之前,請大家牢記「千萬不要對已經 `push` 到 **遠端儲存庫(remote repository)** 的提交進行 `rebase` 」,這是很危險的!詳情請見 `The Perils of Rebasing `_ 7 | 8 | 改變過去是很危險的!請牢記在心! 9 | 10 | 接下來,本文會說明什麼是 `rebase` ,並且介紹 `rebase` 的幾個基本用法。 11 | 12 | ====================== 13 | 什麼是 rebase 14 | ====================== 15 | 16 | Rebase 對於很多人來說是一個很抽象的概念,也因此它的學習門檻就在於如何了解這個抽象的概念。對於 `rebase` 比較恰當的比喻應該是「移花接木」,簡單來講把你的分支接到別的分支上,稍後我們用幾個圖來示範 merge 與 `rebase` 的差異。 17 | 18 | 了解 `rebase` 之前,我們必須了解什麼是 `base` 。對 Git 的使用者而言,在分支中進行開發活動是稀鬆平常的事情,也因此在合併管理分支時,也就需要了解分支是在哪個時間點哪個提交點分出來的旁支,而長出旁支來的提交點,對於旁支來說就是 `base commit` ,也就是 `base` 。所以簡單來說, `rebase` 其實就是改變分支的 `base` 的功能。 19 | 20 | 下圖是在 merge 的情況會產生的版本演進的示意圖,可以看到在新的分支中所做的變更,在合併之後,一併成為一個新的提交(commit 6)。而 `commit 1` 就是 New Branch 的 `base` 。 21 | 22 | .. figure:: images/merge.png 23 | :align: center 24 | 25 | Merge 的示意 26 | 27 | 而下圖是 `rebase` 的情況下會產生的版本演進的示意圖。我們同樣是在分支中進行開發的動作,但是在 `rebase` 時,與 merge 不同的是,Git 會將分支上所做的變更先暫存起來,接著把 `newbase` (或稱新基準點)合併進來,最後直接將剛剛暫存起來的變更在分支上重演,這邊用「重演」這個字眼是表示「 **rebase 不是將提交(commit)複製到分支上,而是將整個變更過程一個一個重新套用到分支上** 」,也就因為如此 `commit 2'` 與 `commit 3'` ,才會有另外的 `'` 符號表示與原本的 `commit 2` , `commit 3` 不同,這點可以從 commit 的 SHA1 湊值不同看出來,雖然變更的內容相同,但是 commit 編號是不同的。本文會在稍後利用範例演示一遍。 28 | 29 | .. figure:: images/rebase.png 30 | :align: center 31 | 32 | Rebase 的示意 33 | 34 | 也就因為如此,所以 `rebase` 的行為就很像「移花接木」,以上圖來說,就是把 New Branch 的變更整個接到 Master 上。 35 | 36 | 37 | 接下來的文章中,將會傳授 `rebase` 的 2 大主軸: 38 | 39 | - 基礎用法 40 | - 進階互動模式 41 | 42 | 在基礎用法中,我們將教授 `rebase` 分支剪接的部分。 43 | 44 | 而進階互動模式中將會說明如何交換提交次序、修改提交內容、合併提交內容,甚至將一個提交拆解成多個提交。 45 | 46 | ========================== 47 | Rebase - 基礎用法 48 | ========================== 49 | 50 | 以下我們用一個情境示範 `rebase` 的「基礎用法」: 51 | 52 | 你是一位 team leader,你的其中一項職務就是負責進行程式碼審查(code review),並且將不同程式分支進行合併管理。 53 | 54 | 現在有 2 位程式設計師以 `develop` 分支為基礎,分別開了新的分支 `feature-a` 與 `feature-b` ,也都已經完工了。 55 | 你希望利用 `rebase` 的方式將這 2 個分支併入 `develop` 中。 56 | 57 | 58 | 首先, `develop` 的日誌如下所示: 59 | 60 | :: 61 | 62 | commit 38844ba14312c642dcd0f72baf031de0c50ad736 63 | Author: one.man.army 64 | Date: Mon Sep 22 15:41:04 2014 +0800 65 | 66 | add HelloWorld.c 67 | 68 | commit 3908e6bc1007f12566fdb5a0fb43f4055560b880 69 | Author: one.man.army 70 | Date: Mon Sep 22 15:40:42 2014 +0800 71 | 72 | initial commit 73 | 74 | 75 | 接著, `feature-a` 的日誌如下所示: 76 | 77 | :: 78 | 79 | commit 15bf9c8954633700211f5b9d246ae67d8135cf29 80 | Author: one.man.army 81 | Date: Mon Sep 22 15:42:48 2014 +0800 82 | 83 | add feature_a.c 84 | 85 | commit 38844ba14312c642dcd0f72baf031de0c50ad736 86 | Author: one.man.army 87 | Date: Mon Sep 22 15:41:04 2014 +0800 88 | 89 | add HelloWorld.c 90 | 91 | commit 3908e6bc1007f12566fdb5a0fb43f4055560b880 92 | Author: one.man.army 93 | Date: Mon Sep 22 15:40:42 2014 +0800 94 | 95 | initial commit 96 | 97 | 98 | 最後是 `feature-b` 的日誌: 99 | 100 | :: 101 | 102 | commit e9d7a6f8b27bca86ef298911d84891b8a7efeada 103 | Author: one.man.army 104 | Date: Mon Sep 22 15:45:37 2014 +0800 105 | 106 | add #include 107 | 108 | commit eb6436b59b7a0624f3ec5e5469ac36b37b5211e7 109 | Author: one.man.army 110 | Date: Mon Sep 22 15:43:55 2014 +0800 111 | 112 | add feature_b.c 113 | 114 | commit 38844ba14312c642dcd0f72baf031de0c50ad736 115 | Author: one.man.army 116 | Date: Mon Sep 22 15:41:04 2014 +0800 117 | 118 | add HelloWorld.c 119 | 120 | commit 3908e6bc1007f12566fdb5a0fb43f4055560b880 121 | Author: one.man.army 122 | Date: Mon Sep 22 15:40:42 2014 +0800 123 | 124 | initial commit 125 | 126 | 127 | 可以看到 `feature-a` 與 `feature-b` 分別比 `develop` 多出了 1, 2 個提交。 128 | 129 | 身為一名專業的 team leader,我們有著足夠的信心,相信這 2 個分支運作的很好,因此我們用以下指令進行 `rebase` 。 130 | 131 | :: 132 | 133 | $ git checkout develop 134 | $ 135 | $ git rebase feature-a 136 | First, rewinding head to replay your work on top of it... 137 | Fast-forwarded develop to feature-a. 138 | $ 139 | $ git rebase feature-b 140 | First, rewinding head to replay your work on top of it... 141 | Applying: add feature_a.c 142 | 143 | 144 | 在上述指令中,我們先切換到 `develop` 分支中,接著我們很快的就利用指令 `git rebase ` 合併了 `feature-a` 與 `feature-b` 。此外,在上述的指令執行結果中,可以看到一行訊息顯示 `Fast-forwarded develop to feature-a` ,其中的 `Fast-forwarded` 是什麼意思呢? 145 | 146 | `Fast-forwarded` 指的就是當 2 個分支的頭尾相接時,代表 2 者之間不會有 conflict ,因此只要改 HEAD 的指向就能夠迅速合併了。以本情境為例, `develop` 的最後一個提交正好是 `feature-a` 的頭,所以這兩者的 `rebase` 適用 `Fast-forwarded` 模式。 147 | 148 | 接下來,可以用 `git log` 看看 `develop` 的日誌,我們可以從日誌中發現 `feature-a` 與 `feature-b` 的 `commit ID` 都不一樣了。 149 | 150 | :: 151 | 152 | $ git log 153 | commit 07ef0b8e0b1edd079fb8b69f6e6e215725b5aba4 154 | Author: spitfire-sidra 155 | Date: Mon Sep 22 15:42:48 2014 +0800 156 | 157 | add feature_a.c 158 | 159 | commit e9d7a6f8b27bca86ef298911d84891b8a7efeada 160 | Author: spitfire-sidra 161 | Date: Mon Sep 22 15:45:37 2014 +0800 162 | 163 | add #include 164 | 165 | commit eb6436b59b7a0624f3ec5e5469ac36b37b5211e7 166 | Author: spitfire-sidra 167 | Date: Mon Sep 22 15:43:55 2014 +0800 168 | 169 | add feature_b.c 170 | 171 | commit 38844ba14312c642dcd0f72baf031de0c50ad736 172 | Author: spitfire-sidra 173 | Date: Mon Sep 22 15:41:04 2014 +0800 174 | 175 | add HelloWorld.c 176 | 177 | commit 3908e6bc1007f12566fdb5a0fb43f4055560b880 178 | Author: spitfire-sidra 179 | Date: Mon Sep 22 15:40:42 2014 +0800 180 | 181 | initial commit 182 | 183 | 以上就是最簡單的 `rebase` 過程。 184 | 185 | 但是在這過程中,有些人可能產生了幾個疑問——「為什麼先 `rebase feature-a` 再 `rebase feature-b` 後,會是 `feature-a` 的日誌在最上方呢?」 186 | 187 | 這是由於 `rebase` 會先找出與 `newbase` 之間最近的一個共同 `base` ,然後先保留 HEAD 所在分支(也就是當前分支)從共同 `base` 開始的所有變更,接著從共同 `base` 開始,將 `newbase` 的變更重新套用到 HEAD 的所在分支後,再將方才所保留的當前分支變更一個一個套用進來,也因此 `feature-a` 會是最後的一個 `commit` 。 188 | 189 | 我們一樣以圖示進行說明。下圖 **After rebase feature-a** 是 `rebase feature-a` 之後的樣子,可以看到 `rebase feature-a` 之後 `develop` 與 `feature-b` 的共同 `base` 是 `commit 38844b` ,因此如果要再 `rebase feature-b` 的話, `commit 15bf9c` 會先被暫存起來,先進行 `rebase feature-b` 之後,再將剛剛暫存的 `commit 38844b` 重演一次,所以在圖 **After rebase feature-b** 中 `feature-a` 的 `commit ID` 就從 `338844b` 變成 `07ef0b` ,這就是 `rebase` 的過程了。 190 | 191 | .. figure:: images/rebase_feature_a.png 192 | :align: center 193 | 194 | After rebase feature-a 195 | 196 | 197 | .. figure:: images/rebase_feature_b.png 198 | :align: center 199 | 200 | After rebase feature-b 201 | 202 | 203 | 問題又來了,剛剛學的 `rebase` 會將整個分支都接上去,有時候我們不需要整個分支都接上去,只要接到分支上的某個提交的點即可,這種情況下可以使用 `rebase --onto` 進行。 204 | 205 | 假設只需要接到 `feature-b` 的 `commit eb6436` 時,就可以用以下指令進行 `rebase` : 206 | 207 | :: 208 | 209 | $ git rebase feature-b --onto eb6436 210 | 211 | 212 | 又或者,想要把我們現在的分支整個接到某個分支點上面時,可以選擇另一種用法: 213 | 214 | :: 215 | 216 | $ git rebase --onto 217 | 218 | 例如,我們在 `feature-b` 分支上時,想把整個分支接到 `commit 3908e6` (initial commit) 時,可以輸入以下指令: 219 | 220 | :: 221 | 222 | $ git co feature-b #先切換到 feature-b 223 | $ git rebase --onto 3908e6 38844b 224 | 225 | 下面 2 張圖就是執行上述指令的前後對照。 226 | 227 | .. figure:: images/before_rebase_onto.png 228 | :align: center 229 | 230 | before rebase --onto 3908e6 38844b 231 | 232 | 233 | .. figure:: images/after_rebase_onto.png 234 | :align: center 235 | 236 | after rebase --onto 3908e6 38844b 237 | 238 | ========================== 239 | Rebase - 進階互動模式 240 | ========================== 241 | 242 | 243 | `Rebase` 的互動模式十分強大,可以允許我們交換提交的次序、修改提交內容、合併提交內容,甚至將一個提交拆解成多個提交。 244 | 245 | 要進入互動模式的基本指令如下, `base commit` 可以是分支上的任意一點: 246 | 247 | :: 248 | 249 | $ git rebase -i 250 | 251 | 例如,我們想利用互動模式將 `feature-b` 上的提交做一些整理時,就可以用以下指令進入互動模式: 252 | 253 | :: 254 | 255 | $ git rebase -i 38844b 256 | 257 | 上述指令的意思就是我們希望將 `feature-b` 從 `commit 38844b` 之後的所有提交(不含 `commit 38844b` )進行整理。 258 | 259 | 接著就會出現類似以下的訊息: 260 | 261 | :: 262 | 263 | pick 1011f14 add feature_b.c 264 | pick d26076a add #include 265 | 266 | # Rebase 38844ba..d26076a onto 38844ba 267 | # 268 | # Commands: 269 | # p, pick = use commit 270 | # r, reword = use commit, but edit the commit message 271 | # e, edit = use commit, but stop for amending 272 | # s, squash = use commit, but meld into previous commit 273 | # f, fixup = like "squash", but discard this commit's log message 274 | # x, exec = run command (the rest of the line) using shell 275 | # 276 | # These lines can be re-ordered; they are executed from top to bottom. 277 | # 278 | # If you remove a line here THAT COMMIT WILL BE LOST. 279 | # 280 | # However, if you remove everything, the rebase will be aborted. 281 | # 282 | # Note that empty commits are commented out 283 | 284 | 在進一步操作前,我們必須對訊息上的幾個指令(commands)進行說明: 285 | 286 | :pick: 287 | 保留此提交 288 | 289 | :reword: 290 | 修改提交的訊息(只改提交訊息) 291 | 292 | :edit: 293 | 保留此提交,但是需要做一些修改(例如在程式裡面多加些註解) 294 | 295 | :squash: 296 | 保留此提交,但是將上面的提交一併併入此提交,此動作會顯示提交訊息供人編輯 297 | 298 | :fixup: 299 | 與squash相似,但是此提交的提交訊息會被上面的提交訊息取代 300 | 301 | :exec: 302 | 執行 shell 指令,例如 **exec make test** 進行一些測試,可以隨意穿插在提交點之間 303 | 304 | 305 | --------------------- 306 | 變換順序 307 | --------------------- 308 | 309 | 接下來,簡單示範變換提交的順序,此處我們想把提交的順序變成先 `commit 1011f14` 再來才是 `commit d26076a` ,我們只要簡單將上述的 `rebase` 訊息換成如下的訊息,也就是兩行互換即可,就能夠變換順序了! 310 | 311 | :: 312 | 313 | # 此處調換次序即可 314 | pick d26076a add #include 315 | pick 1011f14 add feature_b.c 316 | 317 | # Rebase 38844ba..d26076a onto 38844ba 318 | # 319 | # Commands: 320 | # p, pick = use commit 321 | # r, reword = use commit, but edit the commit message 322 | # e, edit = use commit, but stop for amending 323 | # s, squash = use commit, but meld into previous commit 324 | # f, fixup = like "squash", but discard this commit's log message 325 | # x, exec = run command (the rest of the line) using shell 326 | # 327 | # These lines can be re-ordered; they are executed from top to bottom. 328 | # 329 | # If you remove a line here THAT COMMIT WILL BE LOST. 330 | # 331 | # However, if you remove everything, the rebase will be aborted. 332 | # 333 | # Note that empty commits are commented out 334 | 335 | 336 | ------------------- 337 | 修改提交內容 338 | ------------------- 339 | 340 | 有些時候,我們提交之後,不免會註解忘了加或是程式內還有測試的code忘記清掉。這時候除了用 `git reset --soft HEAD^` 之外,也可以用 `rebase` 編輯那些需要修正的提交。 341 | 342 | 例如,我們希望用 `rebase` 在 `commit 1011f14` 中添加幾個提交,就可以將 `pick` 改成 `edit` 進入編輯狀態。 343 | 344 | :: 345 | 346 | pick 1011f14 add feature_b.c 347 | edit d26076a add #include 348 | 349 | # Rebase 38844ba..d26076a onto 38844ba 350 | # 351 | # Commands: 352 | # p, pick = use commit 353 | # r, reword = use commit, but edit the commit message 354 | # e, edit = use commit, but stop for amending 355 | # s, squash = use commit, but meld into previous commit 356 | # f, fixup = like "squash", but discard this commit's log message 357 | # x, exec = run command (the rest of the line) using shell 358 | # 359 | # These lines can be re-ordered; they are executed from top to bottom. 360 | # 361 | # If you remove a line here THAT COMMIT WILL BE LOST. 362 | # 363 | # However, if you remove everything, the rebase will be aborted. 364 | # 365 | # Note that empty commits are commented out 366 | 367 | 接下來,如果用 `git status` 就可以看到我們正在 `rebase` 的訊息: 368 | 369 | :: 370 | 371 | $ git status 372 | rebase in progress; onto 38844ba 373 | You are currently editing a commit while rebasing branch 'Feature-B' on '38844ba'. 374 | (use "git commit --amend" to amend the current commit) 375 | (use "git rebase --continue" once you are satisfied with your changes) 376 | 377 | nothing to commit, working directory clean 378 | 379 | **如果你只是想修正提交訊息** ,就可以用以下指令 : 380 | 381 | :: 382 | 383 | $ git commit --amend 384 | 385 | 386 | **如果你需要多增加幾個提交,直接編輯吧** ,接著用 `git add ` , `git commit -m ` 等一般操作進行。最後再利用以下指令完成 `rebase` : 387 | 388 | :: 389 | 390 | $ git rebase --continue 391 | 392 | 393 | **又或者,我們現在編輯的提交實在是太大了,可能對程式碼審查的人造成困擾,例如同時修正太多個檔案,我們希望拆成比較明確的多個提交** ,就可以用以下指令回到未提交前的狀態: 394 | 395 | :: 396 | 397 | $ git reset HEAD^ 398 | 399 | 然後就可以用 `git status` 列出這個提交中變更了多少檔案,然後依照需求一個一個用 `git add` 加進去後提交,多提交個幾次,就等於是將一個提交拆成多個提交囉!不過別忘了,要用以下指令結束 `rebase` 。 400 | 401 | :: 402 | 403 | $ git rebase --continue 404 | 405 | 以上就是 `rebase` 的幾個簡單說明與操作。 406 | 407 | 至於 `squash` , `fixup` 以及 `exec` 就留給各位去體驗了! 408 | 409 | ================================ 410 | Rebase 出現問題時的處理方法 411 | ================================ 412 | 413 | `Rebase` 與 `merge` 一樣都可能會產生 **conflict** ,這時候除了修正 **conflict** 之後再用 `git add ` , `git rebase --continue` 完成 `rebase` 之外,也可以用 `git rebase --abort` 直接放棄 `rebase` 。 414 | 415 | :: 416 | 417 | git rebase (--continue | --abort | --skip) 418 | 419 | 420 | 此外,對於 `rebase` 使用不慎時,我們會希望能夠直接回復到 `rebase` 之前的狀態,以下就是幾個指令可以用來回復到 `rebase` 之前的狀態。 參考自 StackOverFlow_ 。 421 | 422 | 回復方法 1 : 423 | 424 | :: 425 | 426 | # 最簡單的用法 427 | $ git reset --hard ORIG_HEAD 428 | 429 | 回復方法 2 : 430 | 431 | :: 432 | 433 | # rebase 之前先上 tag 434 | $ git tag BACKUP 435 | $ ... # rebase 過程 436 | $ ... # rebase 過程 437 | $ git reset --hard BACKUP # 失敗的話可以直接回復到 tag BACKUP 438 | 439 | 回復方法 3 : 440 | 441 | :: 442 | 443 | $ git reflog # 尋找要回復的 HEAD ,以下假設是 HEAD@{3} 444 | $ git reset --hard HEAD@{3} # 回復 445 | 446 | .. _StackOverFlow: http://stackoverflow.com/questions/134882/undoing-a-git-rebase 447 | -------------------------------------------------------------------------------- /source/references.rst: -------------------------------------------------------------------------------- 1 | 參考資料 2 | ========= 3 | 4 | #. `Git Documentation `_ , http://git-scm.com/documentation 5 | #. `A successful Git branching model `_ , http://nvie.com/posts/a-successful-git-branching-model/ 6 | #. `GitHub Ignoring files `_ , https://help.github.com/articles/ignoring-files 7 | #. `GitLab `_ , http://gitlab.org/ 8 | #. `Handy Git tips to stop you getting fired `_ , http://blog.apiaxle.com/post/handy-git-tips-to-stop-you-getting-fired/ 9 | #. `Must Have Git Aliases: Advanced Examples `_ , http://durdn.com/blog/2012/11/22/must-have-git-aliases-advanced-examples/ 10 | -------------------------------------------------------------------------------- /source/tagging.rst: -------------------------------------------------------------------------------- 1 | Git 上標籤(Tagging) 2 | ========================= 3 | 4 | 程式的開發幾乎都需要所謂的「版號」,除了開發者之間可用以識別正在開發或維護的程式是否版本相同之外,也可以用版號來表示程式的開發狀態(例如 development, alpha, beta, production/stable 等等),或者是標示重大版本演進(例如從 1.0 演變為 2.0)。 5 | 6 | 不同的開發者或者開發團隊,也都會有不同的版號規則。但目前常見的 2 種版號規則如下: 7 | 8 | - Date based scheme 9 | 10 | - Squence-based scheme 11 | 12 | 更詳細的說明可以參見:http://en.wikipedia.org/wiki/Software_versioning 13 | 14 | 上版號的工作,就可以利用 Git 標籤的功能來達成。以下本文將介紹如何使用 Git 的標籤功能。 15 | 16 | ------------------------- 17 | 新增標籤 & 列出標籤 18 | ------------------------- 19 | 20 | Git 的標籤分為 2 種,其分別為 lightweight 以及 annotated 。 21 | 22 | 1. lightweight 23 | 24 | lightweight 標籤其實就只是一個指標(pointer)指向特定的 commit 而已。 25 | 26 | 新增 lightweight 標籤的方法很簡單: 27 | 28 | :: 29 | 30 | $ git tag my-light-tag 31 | 32 | 新增完標籤之後,就可以用以下指令列出標籤。 33 | 34 | :: 35 | 36 | $ git tag # 列出所有標籤 37 | 38 | 39 | 又或者可以用 ``git show <標籤名>`` 來查看 lightweight 標籤所對應到的 commit 資訊。 40 | 41 | :: 42 | 43 | $ git show my-lightweight-tag 44 | 45 | 46 | 2. annotated 47 | 48 | Git 官方文件推薦使用的標籤。 annotated 標籤則不像 lightweight 標籤,annotated 標籤在建立時也會同時儲存許多資訊,例如上標籤的日期、上標籤者的名字、標籤訊息以及 Checksum 等等,此外還可以加簽(sign)進行驗證。 49 | 50 | annotated 標籤與 lightweight 標籤使用類似,但需要 ``-a `` , ``-m `` 2 個參數,例如: 51 | 52 | :: 53 | 54 | $ git tag -a 'my-annotated-tag' -m 'my first annotated tag!' 55 | 56 | 新增完 annotated 標籤之後就可以使用, ``git tag -n`` 查看: 57 | 58 | :: 59 | 60 | $ git tag -n 61 | my-annotated-tag my first annotated tag 62 | 63 | 預設的標籤是加在當前的 commit,不過也可以為之前的 commit 上標籤,只要加上想加標籤的 commit 編號即可: 64 | 65 | :: 66 | 67 | $ git tag -a '1.0.dev' 3b7de7f 68 | 69 | ------------- 70 | 推送標籤 71 | ------------- 72 | 73 | 標籤的新增都是在本地端(local)進行,所以其他成員或協作者並無法看到你上的標籤,所以就必須將標籤透過 ``git push `` 上傳到遠端 Repository 。 74 | 75 | 例如: 76 | 77 | :: 78 | 79 | $ git push origin my-lightweight-tag 80 | 81 | 如此一來,其他成員就能夠透過遠端 Repository 得到你上的標籤。 82 | 83 | 此外,如果想一次性地將本地端所有標籤上傳,可以使用以下指令: 84 | 85 | :: 86 | 87 | $ git push --tags 88 | 89 | 90 | ------------- 91 | 刪除標籤 92 | ------------- 93 | 94 | 標籤的刪除十分容易,只要用以下指令即可: 95 | 96 | :: 97 | 98 | $ git tag -d 99 | 100 | 該指令只會刪除本地端的標籤。不過如果該標籤已經上傳到遠端 Repository 的話,就得使用另外的指令刪除。 101 | 102 | -------------------------------- 103 | 刪除遠端 Repository 的標籤 104 | -------------------------------- 105 | 106 | 刪除遠端 Repository 的標籤指令為: 107 | 108 | :: 109 | 110 | $ git push :refs/tags/ 111 | 112 | 例如: 113 | 114 | :: 115 | 116 | $ git push origin :refs/tags/my-lightweight-tag 117 | 118 | --------------------- 119 | 利用標籤切換版本 120 | --------------------- 121 | 122 | 在 Git 中無法利用標籤進行版本切換,但是卻可以利用分支的功能,來達到利用標籤進行版本切換的功能。 123 | 124 | 利用標籤切換版本的指令為: 125 | 126 | :: 127 | 128 | $ git checkout -b 129 | 130 | 例如: 131 | 132 | :: 133 | 134 | $ git checkout -b 'v2.0' my-v2-tag 135 | --------------------------------------------------------------------------------