Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 2 commits
  • 22 files changed
  • 0 comments
  • 1 contributor
0  zh/basic.txt → zh_cn/basic.txt
File renamed without changes
0  zh/branch.txt → zh_cn/branch.txt
File renamed without changes
0  zh/clone.txt → zh_cn/clone.txt
File renamed without changes
0  zh/drawbacks.txt → zh_cn/drawbacks.txt
File renamed without changes
0  zh/grandmaster.txt → zh_cn/grandmaster.txt
File renamed without changes
0  zh/history.txt → zh_cn/history.txt
File renamed without changes
0  zh/intro.txt → zh_cn/intro.txt
File renamed without changes
0  zh/multiplayer.txt → zh_cn/multiplayer.txt
File renamed without changes
0  zh/preface.txt → zh_cn/preface.txt
File renamed without changes
0  zh/secrets.txt → zh_cn/secrets.txt
File renamed without changes
0  zh/translate.txt → zh_cn/translate.txt
File renamed without changes
224  zh_tw/basic.txt
... ...
@@ -0,0 +1,224 @@
  1
+== 基本技巧 ==
  2
+
  3
+與其一頭紮進Git命令的海洋中,不如來點基本的例子試試手。它們簡單而且實用。實際
  4
+上,在開始使用Git的頭幾個月,我所用的從來沒超出本章介紹的內容。
  5
+
  6
+=== 保存狀態 ===
  7
+
  8
+要不來點猛的?在做之前,先為當前目錄所有檔案做個快照,使用:
  9
+
  10
+ $ git init
  11
+ $ git add .
  12
+ $ git commit -m "My first backup"
  13
+
  14
+現在如果你的編輯亂了套,恢復之前的版本:
  15
+
  16
+ $ git reset --hard
  17
+
  18
+再次保存狀態:
  19
+
  20
+ $ git commit -a -m "Another backup"
  21
+
  22
+=== 添加、刪除、重命名 ===
  23
+
  24
+以上命令將只跟蹤你第一次運行 *git add* 命令時就已經存在的檔案。如果要添加新文
  25
+件或子目錄,你需要告訴Git:
  26
+
  27
+ $ git add readme.txt Documentation
  28
+
  29
+類似,如果你想讓Git忘記某些檔案:
  30
+
  31
+ $ git rm kludge.h obsolete.c
  32
+ $ git rm -r incriminating/evidence/
  33
+
  34
+這些檔案如果還沒刪除,Git刪除它們。
  35
+
  36
+重命名檔案和先刪除舊檔案,再添加新檔案的一樣。也有一個快捷方式 *git mv* ,和
  37
+*mv* 命令的用法一樣。例如:
  38
+
  39
+ $ git mv bug.c feature.c
  40
+
  41
+=== 進階撤銷/重做 ===
  42
+
  43
+有時候你只想把某個時間點之後的所有改動都回滾掉,因為這些的改動是不正確的。那
  44
+麼:
  45
+
  46
+ $ git log
  47
+
  48
+來顯示最近提交列表,以及他們的SHA1哈希值:
  49
+
  50
+----------------------------------
  51
+commit 766f9881690d240ba334153047649b8b8f11c664
  52
+Author: Bob <bob@example.com>
  53
+Date:   Tue Mar 14 01:59:26 2000 -0800
  54
+
  55
+    Replace printf() with write().
  56
+
  57
+commit 82f5ea346a2e651544956a8653c0f58dc151275c
  58
+Author: Alice <alice@example.com>
  59
+Date:   Thu Jan 1 00:00:00 1970 +0000
  60
+
  61
+    Initial commit.
  62
+----------------------------------
  63
+
  64
+哈希值的前幾個字元足夠確定一個提交;也可以拷貝粘貼完整的哈希值,鍵入:
  65
+
  66
+ $ git reset --hard 766f
  67
+
  68
+來恢復到一個指定的提交狀態,並從記錄裡永久抹掉所有比該記錄新一些的提交。
  69
+
  70
+另一些時候你想簡單地跳到一個舊狀態。這種情況,鍵入:
  71
+
  72
+ $ git checkout 82f5
  73
+
  74
+這個操作將把你帶回過去,同時也保留較新提交。然而,像科幻電影裡時光旅行一樣,
  75
+如果你這時編輯並提交的話,你將身處另一個現實裡,因為你的動作與開始時相比是不
  76
+同的。
  77
+
  78
+這另一個現實叫作“分支”(branch),之後 <<branch,我們會對這點多討論一些>>。
  79
+至于現在,只要記住:
  80
+
  81
+ $ git checkout master
  82
+
  83
+會把你帶到當下來就可以了。另外,為避免Git的抱怨,應該在每次運行checkout之前提
  84
+交(commit)或重置(reset)你的改動。
  85
+
  86
+還以電腦遊戲作為類比:
  87
+
  88
+- *`git reset --hard`*: 加載一個舊記錄並刪除所有比之新的記錄。
  89
+
  90
+- *`git checkout`*: 加載一個舊記錄,但如果你在這個記錄上玩,遊戲狀態將偏離第
  91
+  一輪的較新狀態。你現在打的所有遊戲記錄會在你剛進入的、代表另一個真實的分支
  92
+  裡。<<branch,我們稍後論述>>。
  93
+
  94
+你可以選擇只恢復特定檔案和目錄,通過將其加在命令之後:
  95
+
  96
+ $ git checkout 82f5 some.file another.file
  97
+
  98
+小心,這種形式的 *checkout* 會不聲不響地覆蓋檔案。為阻止意外發生,在運行任何
  99
+checkout命令之前做提交,尤其在初學Git的時候。通常,任何時候你覺得對運行某個命
  100
+令不放心,無論Git命令還是不是Git命令,就先運行一下 *git commit -a* 。
  101
+
  102
+不喜歡拷貝站題哈希值?那就用:
  103
+
  104
+ $ git checkout :/"My first b"
  105
+
  106
+來跳到以特定字元串開頭的提交。你也可以回到倒數第五個保存狀態:
  107
+
  108
+ $ git checkout master~5
  109
+
  110
+=== 撤銷 ===
  111
+
  112
+在法庭上,事件可以從法庭記錄裡敲出來。同樣,你可以檢出特定提交以撤銷。
  113
+
  114
+ $ git commit -a
  115
+ $ git revert 1b6d
  116
+
  117
+講撤銷給定哈希值的提交。本撤銷被記錄為一個新的提交,你可以通過運行 *git log*
  118
+來確認這一點。
  119
+
  120
+=== 變更日誌生成 ===
  121
+
  122
+一些項目要求生成變更日誌http://en.wikipedia.org/wiki/Changelog[changelog]. 生
  123
+成一個,通過鍵入:
  124
+
  125
+ $ git log > ChangeLog
  126
+
  127
+=== 下載檔案 ===
  128
+
  129
+得到一個由Git管理的項目的拷貝,通過鍵入:
  130
+
  131
+ $ git clone git://server/path/to/files
  132
+
  133
+例如,得到我用來創建該站的所有檔案:
  134
+
  135
+ $ git clone git://git.or.cz/gitmagic.git
  136
+
  137
+我們很快會對 *clone* 命令談的很多。
  138
+
  139
+=== 到最新 ===
  140
+
  141
+如果你已經使用 *git clone* 命令得到了一個項目的一份拷貝,你可以更新到最新版,
  142
+通過:
  143
+
  144
+ $ git pull
  145
+
  146
+ 
  147
+=== 快速發佈 ===
  148
+
  149
+假設你寫了一個腳本,想和他人分享。你可以只告訴他們從你的計算機下載,但如果此
  150
+時你正在改進你的腳本,或加入試驗性質的改動,他們下載了你的腳本,他們可能由此
  151
+陷入困境。當然,這就是發佈周期存在的原因。開發人員可能頻繁進行項目修改,但他
  152
+們只在他們覺得代碼可以見人的時候才擇時發佈。
  153
+
  154
+用Git來完成這項,需要進入你的腳本所在目錄:
  155
+
  156
+ $ git init
  157
+ $ git add .
  158
+ $ git commit -m "First release"
  159
+
  160
+然後告訴你的用戶去運行:
  161
+
  162
+ $ git clone your.computer:/path/to/script
  163
+
  164
+來下載你的腳本。這要假定他們有ssh訪問權限。如果沒有,需要運行 *git daemon* 並
  165
+告訴你的用戶去運行:
  166
+
  167
+ $ git clone git://your.computer/path/to/script
  168
+
  169
+從現在開始,每次你的腳本準備好發佈時,就運行:
  170
+
  171
+ $ git commit -a -m "Next release"
  172
+
  173
+並且你的用戶可以通過進入包含你腳本的目錄,並鍵入下列命令,來更新他們的版本:
  174
+
  175
+ $ git pull
  176
+
  177
+你的用戶永遠也不會取到你不想讓他們看到的腳本版本。顯然這個技巧對所有的東西都
  178
+是可以,不僅是對腳本。
  179
+
  180
+
  181
+=== 我們已經做了什麼? ===
  182
+
  183
+找出自從上次提交之後你已經做了什麼改變:
  184
+
  185
+ $ git diff
  186
+
  187
+或者自昨天的改變:
  188
+
  189
+ $ git diff "@{yesterday}"
  190
+
  191
+或者一個特定版本與倒數第二個變更之間:
  192
+
  193
+ $ git diff 1b6d "master~2"
  194
+
  195
+輸出結果都是補丁格式,可以用 *git apply* 來把補丁打上。也可以試一下:
  196
+
  197
+ $ git whatchanged --since="2 weeks ago"
  198
+
  199
+我也經常用http://sourceforge.net/projects/qgit[qgit] 瀏覽歷史, 因為他的圖形界
  200
+面很養眼,或者 http://jonas.nitro.dk/tig/[tig] ,一個文本界面的東西,很慢的網
  201
+絡狀況下也工作的很好。也可以安裝web 伺服器,運行 *git instaweb* ,就可以用任
  202
+何瀏覽器瀏覽了。
  203
+
  204
+=== 練習 ===
  205
+
  206
+比方A,B,C,D是四個連續的提交,其中B與A一樣,除了一些檔案刪除了。我們想把這
  207
+些刪除的檔案加回D。我們如何做到這個呢?
  208
+
  209
+至少有三個解決方案。假設我們在D:
  210
+
  211
+  1. A與B的差別是那些刪除的檔案。我們可以創建一個補丁代表這些差別,然後吧補丁
  212
+     打上:
  213
+
  214
+   $ git diff B A | git apply
  215
+
  216
+  2. 既然這些檔案存在A,我們可以把它們拿出來:
  217
+
  218
+   $ git checkout A foo.c bar.h
  219
+
  220
+  3. 我們可以把從A到B的變化視為可撤銷的變更:
  221
+
  222
+   $ git revert B
  223
+
  224
+哪個選擇最好?這取決於你的喜好。利用Git滿足自己需求是容易,經常還有多個方法。
236  zh_tw/branch.txt
... ...
@@ -0,0 +1,236 @@
  1
+== 分支巫術 ==
  2
+
  3
+即時分支合併是Git最給力的殺手鐧。
  4
+
  5
+*問題* :外部因素要求必須切換場景。在發佈版本中突然蹦出個嚴重缺陷。某個特性完
  6
+成的截至日期就要來臨。在項目關鍵部分可以提供幫助的一個開發正打算離職。所有情
  7
+況逼迫你停下所有手頭工作,全力撲到到這個完全不同的任務上。
  8
+
  9
+打斷思維的連續性會使你的生產力大大降低,並且切換上下文也更麻煩,更大的損失。
  10
+使用中心版本控制我們必須從中心伺服器下載一個新的工作拷貝。分散式系統的情況就
  11
+好多了,因為我們能夠在本地克隆所需要的版本。
  12
+
  13
+但是克隆仍然需要拷貝整個工作目錄,還有直到給定點的整個歷史記錄。儘管Git使用文
  14
+件共享和硬連結減少了花費,項目檔案自身還是必須在新的工作目錄裡重建。
  15
+
  16
+*方案* :Git有一個更好的工具對付這種情況,比克隆快多了而且節省空間: *git
  17
+ branch* 。
  18
+
  19
+使用這個魔咒,目錄裡的檔案突然從一個版本變到另一個。除了只是在歷史記錄裡上跳
  20
+下竄外,這個轉換還可以做更多。你的檔案可以從上一個發佈版變到實驗版本到當前開
  21
+發版本到你朋友的版本等等。
  22
+
  23
+=== 老闆鍵 ===
  24
+
  25
+曾經玩過那樣的遊戲嗎?按一個鍵(“老闆鍵”),屏幕立即顯示一個電子表格或別的?
  26
+那麼如果老闆走進辦公室,而你正在玩遊戲,就可以快速將遊戲藏起來。
  27
+
  28
+在某個目錄:
  29
+
  30
+ $ echo "I'm smarter than my boss" > myfile.txt
  31
+ $ git init
  32
+ $ git add .
  33
+ $ git commit -m "Initial commit"
  34
+
  35
+我們已經創建了一個Git倉庫,該倉庫記錄一個包含特定信息的檔案。現在我們鍵入:
  36
+
  37
+ $ git checkout -b boss  # 之後似乎沒啥變化
  38
+ $ echo "My boss is smarter than me" > myfile.txt
  39
+ $ git commit -a -m "Another commit"
  40
+
  41
+看起來我們剛剛只是覆蓋了原來的檔案並提交了它。但這是個錯覺。鍵入:
  42
+
  43
+ $ git checkout master  # 切到檔案的原先版本
  44
+
  45
+嘿真快!這個檔案就恢復了。並且如果老闆決定窺視這個目錄,鍵入:
  46
+
  47
+ $ git checkout boss  # 切到適合老闆看的版本
  48
+
  49
+你可以在兩個版本之間相切多少次就切多少次,而且每個版本都可以獨立提交。
  50
+
  51
+=== 骯髒的工作 ===
  52
+
  53
+[[branch]]
  54
+
  55
+比如你正在開發某個特性,並且由於某種原因,你需要回退三個版本,臨時加進幾行打
  56
+印語句來,來看看一些東西是如何工作的。那麼:
  57
+
  58
+ $ git commit -a
  59
+ $ git checkout HEAD~3
  60
+
  61
+現在你可以到處加醜陋的臨時代碼。你甚至可以提交這些改動。當你做完的時候,
  62
+
  63
+ $ git checkout master
  64
+
  65
+來返回到你原來的工作。看,所有未提交變更都結轉了。
  66
+
  67
+如果你後來想保存臨時變更怎麼辦?簡單:
  68
+
  69
+ $ git checkout -b dirty
  70
+
  71
+只要在切換到主分支之前提交就可以了。無論你什麼時候想回到髒的變更,只需鍵入:
  72
+
  73
+ $ git checkout dirty
  74
+
  75
+我們在前面章節討論加載舊狀態的時候,曾經接觸過這個命令。最終我們把故事說全:
  76
+檔案改變成請求的狀態,但我們必須離開主分支。從現在開始的任何提交都會將你的文
  77
+件提交到另一條不同的路,這個路可以之後命名。
  78
+
  79
+換一個說法,在checkout一個舊狀態之後,Git自動把你放到一個新的,未命名的分支,
  80
+這個分支可以使用 *git checkout -b* 來命名和保存。
  81
+
  82
+=== 快速修訂 ===
  83
+
  84
+你正在做某件事的當間,被告知先停所有的事情,去修理一個新近發現的臭蟲,這個臭
  85
+蟲在提交 `1b6d...`:
  86
+
  87
+ $ git commit -a
  88
+ $ git checkout -b fixes 1b6d
  89
+
  90
+那麼一旦你修正了這個臭蟲:
  91
+
  92
+ $ git commit -a -m "Bug fixed"
  93
+ $ git checkout master
  94
+
  95
+並可以繼續你原來的任務。你甚至可以“合併”到最新修訂:
  96
+
  97
+ $ git merge fixes
  98
+
  99
+=== 合併 ===
  100
+
  101
+一些版本控制系統,創建分支很容易,但把分支合併回來很難。使用Git,合併簡直是家
  102
+常便飯,以至于甚至你可能對其發生沒有察覺。
  103
+
  104
+我們很久之前就遇到合併了。 *pull* 命令取出提交併合並它們到你的當前分支。如果
  105
+你沒有本地變更,那這個合併就是一個“快進”,相當於中心式版本控制系統裡的一個
  106
+弱化的獲取最新版本操作。但如有本地變更,Git將自動合併,並報告任何衝突。
  107
+
  108
+通常,一個提交只有一個“父提交”,也叫前一個提交。合併分支到一起產生一個至少
  109
+有兩個父的提交。這就引出了問題: `HEAD~10` 真正指哪個提交?一個提交可能有多個
  110
+父,那我們跟哪個呢?
  111
+
  112
+原來這個表示每次選擇第一個父。這是可取的,因為在合併時候當前分支成了第一個父;
  113
+多數情況下我們只關注我們在當前分支都改了什麼,而不是從其他分支合併來的變更。
  114
+
  115
+你可以用插入符號來特別指定父。比如,顯示來自第二個父的日誌:
  116
+
  117
+ $ git log HEAD^2
  118
+
  119
+你可以忽略數字以指代第一個父。比如,顯示與第一個父的差別:
  120
+
  121
+ $ git diff HEAD^
  122
+
  123
+你可以結合其他類型使用這個記號。比如:
  124
+
  125
+ $ git checkout 1b6d^^2~10 -b ancient
  126
+
  127
+開始一個新分支 ``ancient'' ,表示第一個父的第二個父的倒數第十次提交的狀態。
  128
+
  129
+=== 不間斷工作流 ===
  130
+
  131
+經常在硬件項目裡,計劃的第二步必須等第一步完成才能開始。待修的汽車傻等在車庫
  132
+裡,直到特定的零件從工廠運來。一個原型在其可以構建之前,可能苦等晶片成型。
  133
+
  134
+軟件項目可能也類似。新功能的第二部分不得不等待,直到第一部分發佈並通過測試。
  135
+一些項目要求你的代碼需要審批才能接受,因此你可能需要等待第一部分得到批准,才
  136
+能開始第二部分。
  137
+
  138
+多虧了無痛分支合併,我們可以不必遵循這些規則,在第一部分正式準備好前開始第二
  139
+部分的工作。假設你已經將第一部分提交並發去審批,比如說你現在在主分支。那麼分
  140
+岔:
  141
+
  142
+ $ git checkout -b part2
  143
+
  144
+接下來,做第二部分,隨時可以提交變更。只要是人就可能犯錯誤,經常你將回到第一
  145
+部分在修修補補。如果你非常幸運,或者超級棒,你可能不必做這幾行:
  146
+
  147
+ $ git checkout master  # 回到第一部分
  148
+ $ 修復問題
  149
+ $ git commit -a        # 提交變更
  150
+ $ git checkout part2   # 回到第二部分
  151
+ $ git merge master     # 合併這些改動
  152
+
  153
+最終,第一部分獲得批准:
  154
+
  155
+ $ git checkout master  # 回到第一部分
  156
+ $ submit files         # 對世界發佈
  157
+ $ git merge part2      # 合併第二部分
  158
+ $ git branch -d part2  # 刪除分支“part2”
  159
+
  160
+現在你再次處在主分支,第二部分的代碼也在工作目錄。
  161
+
  162
+很容易擴展這個技巧,應用到任意數目的部分。它也很容易追溯分支:假如你很晚才意
  163
+識到你本應在7次提交前就創建分支。那麼鍵入:
  164
+
  165
+ $ git branch -m master part2  # 重命名“master”分支為“part2”。
  166
+ $ git branch master HEAD~7    # 以七次前提交建一個新的“master”。
  167
+
  168
+分支 `master` 只有第一部分內容,其他內容在分支 `part2` 。 我們現在後一個分支;
  169
+我們創建了 `master` 分支還沒有切換過去,因為我們想繼續工作在 `part2` 。這是不
  170
+尋常的。直到現在,我們已經在創建之後切換到分支,如:
  171
+
  172
+ $ git checkout HEAD~7 -b master  # 創建分支,並切換過去。
  173
+
  174
+=== 重組雜亂 ===
  175
+
  176
+或許你喜歡在同一個分支下完成工作的方方面面。你想為自己保留工作進度並希望其他
  177
+人只能看到你仔細整理過後的提交。開啟一對分支:
  178
+
  179
+  $ git branch sanitized    # 為乾淨提交創建分支
  180
+  $ git checkout -b medley  # 創建並切換分支以進去工作
  181
+
  182
+接下來,做任何事情:修臭蟲,加特性,加臨時代碼,諸如此類,經常按這種方式提交。
  183
+然後:
  184
+
  185
+  $ git checkout sanitized
  186
+  $ git cherry-pick medley^^
  187
+
  188
+應用分支 ``medley'' 的祖父提交到分支 ``sanitized'' 。通過合適的挑選(像選櫻桃
  189
+那樣)你可以構建一個只包含成熟代碼的分支,而且相關的提交也組織在一起。
  190
+
  191
+=== 管理分支 ===
  192
+
  193
+列出所有分支:
  194
+
  195
+ $ git branch
  196
+
  197
+預設你從叫 ``master'' 的分支開始。一些人主張別碰“master”分支,而是創建你自
  198
+己版本的新分支。
  199
+
  200
+選項 *-d* 和 *-m* 允許你來刪除和移動(重命名)分支。參見 *git help branch* 。
  201
+
  202
+分支``master'' 是一個有用的慣例。其他人可能假定你的倉庫有一個叫這個名字的分
  203
+支,並且該分支包含你項目的官方版本。儘管你可以重命名或抹殺 ``master'' 分支,
  204
+你最好還是尊重這個約定。
  205
+
  206
+=== 臨時分支 ===
  207
+
  208
+很快你會發現你經常會因為一些相似的原因創建短期的分支:每個其它分支只是為了保
  209
+存當前狀態,那樣你就可以直接跳到較老狀態以修復高優先順序的臭蟲之類。
  210
+
  211
+可以和電視的換台做類比,臨時切到別的頻道,來看看其它台那正放什麼。但並不是簡
  212
+單地按幾個按鈕,你不得不創建,檢出,合併,以及刪除臨時分支。幸運的是,Git已經
  213
+有了和電視機遙控器一樣方便的快捷方式:
  214
+
  215
+ $ git stash
  216
+
  217
+這個命令保存當前狀態到一個臨時的地方(一個隱藏的地方)並且恢復之前狀態。你的
  218
+工作目錄看起來和你開始編輯之前一樣,並且你可以修復臭蟲,引入之前變更等。當你
  219
+想回到隱藏狀態的時候,鍵入:
  220
+
  221
+ $ git stash apply  # 你可能需要解決一些衝突
  222
+
  223
+你可以有多個隱藏,並用不同的方式來操作他們。參見 *git help slash* 。也許你已
  224
+經猜到,Git維護在這個場景之後的分支以執行魔法技巧.
  225
+
  226
+=== 按你希望的方式工作 ===
  227
+
  228
+你可能猶疑于分支是否值得一試。畢竟,克隆也几乎一樣快,並且你可以用 *cd* 來在
  229
+彼此之間切換,而不是用Git深奧的命令。
  230
+
  231
+考慮一下瀏覽器。為什麼同時支持多標籤和多窗口?因為允許兩者同時接納納了多種風
  232
+格的用戶。一些用戶喜歡只保持一個打開的窗口,然後用標籤瀏覽多個網頁。一些可能
  233
+堅持另一個極端:任何地方都沒有標籤的多窗口。一些喜好處在兩者之間。
  234
+
  235
+分支類似你工作目錄的標籤,克隆類似打開的瀏覽器新窗口。這些是本地操作很快,那
  236
+為什麼不試着找出最適合你的組合呢?Git讓你按你確實所希望的那樣工作。
228  zh_tw/clone.txt
... ...
@@ -0,0 +1,228 @@
  1
+== 克隆周邊 ==
  2
+
  3
+在較老一代的版本控制系統裡,checkout是獲取檔案的標準操作。你將獲得一組特定保
  4
+存狀態的檔案。
  5
+
  6
+在Git和其他分散式版本控制系統裡,克隆是標準的操作。通過創建整個倉庫的克隆來
  7
+獲得檔案。或者說,你實際上把整個中心伺服器做了個鏡像。凡是主倉庫上能做的事,
  8
+你都能做。
  9
+
  10
+=== 計算機間同步 ===
  11
+
  12
+我可以忍受製作tar包或利用rsync來作備份和基本同步。但我有時在我筆記本上編輯,
  13
+其他時間在台式機上,而且這倆之間也許並不交互。
  14
+
  15
+在一個機器上初始化一個Git倉庫並提交你的檔案。然後轉到另一台機器上:
  16
+
  17
+ $ git clone other.computer:/path/to/files
  18
+
  19
+以創建這些檔案和Git倉庫的第二個拷貝。從現在開始,
  20
+
  21
+ $ git commit -a
  22
+ $ git pull other.computer:/path/to/files HEAD
  23
+
  24
+將把另一台機器上特定狀態的檔案“拉”到你正工作的機器上。如果你最近對同一個文
  25
+件做了有衝突的修改,Git將通知你,而你也應該在解決衝突之後再次提交。
  26
+
  27
+=== 典型源碼控制 ===
  28
+
  29
+為你的檔案初始化Git倉庫:
  30
+
  31
+ $ git init
  32
+ $ git add .
  33
+ $ git commit -m "Initial commit"
  34
+
  35
+在中心伺服器,在某個目錄初始化一個“裸倉庫”:
  36
+
  37
+ $ mkdir proj.git
  38
+ $ cd proj.git
  39
+ $ git init --bare
  40
+ $  # 只用一行命令: GIT_DIR=proj.git git init
  41
+
  42
+如果需要的話,啟動Git守護進程:
  43
+
  44
+ $ git daemon --detach  # 它也許已經在運行了
  45
+
  46
+對一些Git伺服服務,按照其指導來初始化空Git倉庫。一般是在網頁上填一個表單。
  47
+
  48
+把你的項目“推”到中心伺服器:
  49
+ $ git push git://central.server/path/to/proj.git HEAD
  50
+
  51
+撿出源碼,開發鍵入:
  52
+
  53
+ $ git clone git://central.server/path/to/proj.git
  54
+
  55
+做了改動之後,開發保存變更到本地:
  56
+
  57
+ $ git commit -a
  58
+
  59
+更新到最近版本:
  60
+
  61
+ $ git pull
  62
+
  63
+所有衝突應被處理,然後提交:
  64
+
  65
+ $ git commit -a
  66
+
  67
+把本地改動撿入到中心倉庫:
  68
+
  69
+ $ git push
  70
+
  71
+如果主伺服器由於其他開發的活動,有了新的變更,這個撿入會失敗,該開發應該把最
  72
+新版本拿下來,解決合併衝突,然後重試。
  73
+
  74
+=== 裸倉庫 ===
  75
+
  76
+之所以叫裸倉庫是因為其沒有工作目錄;它只包含正常情況下隱藏在`.git`子目錄下
  77
+的檔案。換句話說,它維護項目歷史,而且從不保存任何給定版本的快照。
  78
+
  79
+裸倉庫扮演的角色和中心版本控制系統中中心伺服器的角色類似:你項目的中心。開
  80
+發從其中克隆項目,撿入新近改動。典型地裸倉庫存在一個伺服器上,該伺服器除了
  81
+分散數據外並不做啥。開發活動發生在克隆上,因此中心倉庫沒有工作目錄也行。
  82
+
  83
+
  84
+很多Git命令在裸倉庫上失敗,除非指定倉庫路徑到環境變數`GIT_DIR`,或者指定
  85
+`--bare`選項。
  86
+
  87
+=== 推還是拽 ===
  88
+
  89
+為什麼我們介紹了push命令,而不是依賴熟悉的pull命令?首先,在裸倉庫上pull會
  90
+失敗:除非你必須“fetch”,一個之後我們要討論的命令。但即使我們在中心伺服器上
  91
+保持一個正常的倉庫,拽些東西進去仍然很繁瑣。我們不得不登陸伺服器先,給pull
  92
+命令我們要拽自機器的網絡地址。防火牆會阻礙,並且首先如果我們沒有到伺服器的
  93
+shell訪問怎麼辦呢?
  94
+
  95
+然而,除了這個案例,我們反對推進倉庫,因為當目標有工作目錄時,困惑隨之而來。
  96
+
  97
+簡短截說,學習Git的時候,只在目標是裸倉庫的時候push,否則用pull的方式。
  98
+
  99
+=== 項目分叉 ===
  100
+
  101
+項目走歪了嗎?或者認為你可以做得更好?那麼在伺服器上:
  102
+
  103
+ $ git clone git://main.server/path/to/files
  104
+
  105
+之後告訴每個相關的人你伺服器上項目的分支。
  106
+
  107
+在之後的時間,你可以合併來自原先項目的改變,使用命令:
  108
+
  109
+ $ git pull
  110
+
  111
+=== 終極備份 ===
  112
+
  113
+會有很多散佈在各處,禁止篡改的冗餘存檔嗎? 如果你的項目有很多開發,那乾脆啥也
  114
+別做了。你的每份代碼克隆是一個有效備份。不僅當前狀態,還包括你項目整個歷史。
  115
+感謝哈希加密算法,如果任何人的克隆被損壞,只要他們與其他的交互,這個克隆就會
  116
+被修好。
  117
+
  118
+如果你的項目並不是那麼流行,那就找儘可能多的伺服來放克隆吧。
  119
+
  120
+真正的偏執狂應該總是把HEAD最近20位元組的SHA1哈希值寫到安全的地方。應該保證安全,
  121
+而不是把它藏起來。比如,把它發佈到報紙上就不錯,因為對攻擊者而言,更改每份報
  122
+紙是很難的。
  123
+
  124
+=== 輕快多任務 ===
  125
+
  126
+比如你想並行開發多個功能。那麼提交你的項目並運行:
  127
+
  128
+ $ git clone . /some/new/directory
  129
+
  130
+Git使用硬連結和檔案共享來儘可能安全地創建克隆,因此它一眨眼就完成了,因此你現
  131
+在可以並行操作兩個沒有相互依賴的功能。例如,你可以編輯一個克隆,同時編譯另一
  132
+個。感謝 http://en.wikipedia.org/wiki/Hard_link[hardlinking], 本地克隆比簡單
  133
+備份省時省地。
  134
+
  135
+現在你可以同時工作在兩個彼此獨立的特性上。比如,你可以在編譯一個克隆的時候編
  136
+輯另一個克隆。任何時候,你都可以從其它克隆提交並拖拽變更。
  137
+
  138
+ $ git pull /the/other/clone HEAD
  139
+
  140
+=== 游擊版本控制 ===
  141
+
  142
+你正做一個使用其他版本控制系統的項目, 而你非常思念Git? 那麼在你的工作目錄初
  143
+始化一個Git倉庫:
  144
+
  145
+ $ git init
  146
+ $ git add .
  147
+ $ git commit -m "Initial commit"
  148
+
  149
+然後克隆它:
  150
+
  151
+ $ git clone . /some/new/directory
  152
+
  153
+並在這個目錄工作,按你所想在使用Git。過一會,一旦你想和其他每個人同步,在這種
  154
+情況下,轉到原來的目錄,用其他的版本控制工具同步,並鍵入:
  155
+
  156
+ $ git add .
  157
+ $ git commit -m "Sync with everyone else"
  158
+
  159
+現在轉到新目錄運行:
  160
+
  161
+ $ git commit -a -m "Description of my changes"
  162
+ $ git pull
  163
+
  164
+把你的變更提交給他人的過程依賴于其他版本控制系統。這個新目錄包含你的改動的文
  165
+件。需要運行其他版本控制系統的命令來上載這些變更到中心倉庫。
  166
+
  167
+Subversion, 或許是最好的中心式版本控制系統,為無數項目所用。 *git svn* 命令為
  168
+Subversion倉庫自動化了上面的操作,並且也可以用作
  169
+http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[
  170
+導出Git項目到Subversion倉庫] 的替代。
  171
+
  172
+=== Mercurial ===
  173
+
  174
+Mercurial是一個類似的的版本控制系統,几乎可以和Git一起無縫工作。使用
  175
+`hg-git`插件,一個Mercurial用戶可以無損地往Git倉庫推送,從Git倉庫拖拽。
  176
+
  177
+使用Git獲得`hg-git`插件:
  178
+
  179
+ $ git clone git://github.com/schacon/hg-git.git
  180
+
  181
+或使用Mercurial:
  182
+
  183
+ $ hg clone http://bitbucket.org/durin42/hg-git/
  184
+
  185
+不好意思,我沒注意Git有類似的插件。因此, 我主張使用Git而不是Mercurial作為主資
  186
+源庫,即使你偏愛Mercurial。使用Mercurial項目,通常一個自願者維護一個平行的
  187
+Git項目以適應Git用戶,然而感謝`hg-git`插件,一個Git項目自動地適應Mercurial用
  188
+戶。
  189
+
  190
+儘管該插件可以把一個Mercurial倉庫轉成一個Git倉庫,通過推到一個空的倉庫,
  191
+這個差事交給`hg-fast-export.sh`腳本還是更容易些。來自:
  192
+
  193
+ $ git clone git://repo.or.cz/fast-export.git
  194
+
  195
+要轉化,只需在一個空目錄運行:
  196
+
  197
+ $ git init
  198
+ $ hg-fast-export.sh -r /hg/repo
  199
+
  200
+注意該腳本應加入你的`$PATH`。
  201
+
  202
+=== Bazaar ===
  203
+
  204
+我們簡略提一下Bazaar,它畢竟是緊跟Git和Mercurial之後最流行的自由分散式版本控
  205
+制系統。
  206
+
  207
+Bazaar有後來者的優勢,它相對年輕些;它的設計者可以從前人的錯誤中學習,並且躲
  208
+過去翻歷史上犯過的錯誤。另外,它的開發人員對可移植性以及和與其它版本控制系統
  209
+的互操作性也考慮周全。
  210
+
  211
+一個`bzr-git`插件讓Bazaar用戶在一定程度下可以工作在Git倉庫。`tailor`程序轉
  212
+換Bazaar倉庫到Git倉庫,並且可以遞增的方式做,要知道`bzr-fast-export`只是
  213
+在一次性轉換性情況下工作良好。
  214
+
  215
+=== 我偏愛Git的原因 ===
  216
+
  217
+我起先選擇Git是因為我聽說它能管理不可想象地不可管理的Linux內核源碼。我從來沒
  218
+覺得有離開的必要。Git已經服侍的很好了,並且我也沒有被其瑕疵所困擾。因為我主要
  219
+使用Linux,其他平台上的問題與我無關。
  220
+
  221
+還有,我偏愛C程序和bash腳本,以及諸如Python的可執行可腳本:較少依賴,並且我也
  222
+沉迷于快速的執行時間。
  223
+
  224
+我考慮過Git才能如何提高,甚至自己寫類似的工具,但只作為研究練練手。即使完成這
  225
+個項目,我也無論如何會繼續使用Git,因為使用一個古裡古怪的系統所獲甚微。
  226
+
  227
+自然地,你的需求和期望可能不同,並且你可能使用另一個系統會好些。儘管如此,使
  228
+用Git你都錯不太遠。
133  zh_tw/drawbacks.txt
... ...
@@ -0,0 +1,133 @@
  1
+== 附錄 A: Git的缺點 ==
  2
+
  3
+有一些Git的問題,我已經藏在毯子下面了。有些可以通過腳本或回調方法輕易地解決,
  4
+有些需要重組或重定義項目,少數剩下的煩惱,還只能等待。或者更好地,投入進來幫
  5
+忙。
  6
+
  7
+=== SHA1 的弱點 ===
  8
+
  9
+隨着時間的推移,密碼學家發現越來越多的SHA1的弱點。已經發現對對資源雄厚的組織
  10
+哈希衝撞是可能的。在幾年內,或許甚至一個一般的PC也將有足夠計算能力悄悄摧毀一
  11
+個Git倉庫。
  12
+
  13
+希望在進一步研究摧毀SHA1之前,Git能遷移到一個更好的哈希算法。
  14
+
  15
+=== 微軟 Windows ===
  16
+
  17
+Git在微軟Windows上可能有些繁瑣:
  18
+
  19
+- http://cygwin.com/[Cygwin] ,, 一個Windows下的類Linux的環境,包含一個 http://cygwin.com/packages/git/[ 一個Git在Windows下的移植].
  20
+
  21
+- http://code.google.com/p/msysgit/[基于MSys的Git] 是另一個,要求最小運行時支持,不過一些命令不能馬上工作。
  22
+  
  23
+=== 不相關的檔案 ===
  24
+
  25
+如果你的項目非常大,包含很多不相關的檔案,而且正在不斷改變,Git可能比其他系統
  26
+更不管用,因為獨立的檔案是不被跟蹤的。Git跟蹤整個項目的變更,這通常才是有益的。
  27
+
  28
+一個方案是將你的項目拆成小塊,每個都由相關檔案組成。如果你仍然希望在同一個資
  29
+源庫裡保存所有內容的話,可以使用 *git submodule* 。
  30
+
  31
+=== 誰在編輯什麼? ===
  32
+
  33
+一些版本控制系統在編輯前強迫你顯示地用某個方法標記一個檔案。儘管這種要求很煩
  34
+人,尤其是需要和中心伺服器通訊時,不過它還是有以下兩個好處的:
  35
+
  36
+  1. 比較速度快,因為只有被標記的檔案需要檢查。
  37
+
  38
+  2. 可以知道誰在這個檔案上工作,通過查詢在中心伺服器誰把這個檔案標記為編輯狀
  39
+     態。
  40
+
  41
+使用適當的腳本,你也可以使Git達到同樣的效果。這要求程序員協同工作,當他編輯一
  42
+個檔案的時候還要運行特定的腳本。
  43
+
  44
+=== 檔案歷史 ===
  45
+
  46
+因為Git記錄的是項目範圍的變更,重造單一檔案的變更歷史比其他跟蹤單一檔案的版本
  47
+控制系統要稍微麻煩些。
  48
+
  49
+好在麻煩還不大,也是值得的,因為Git其他的操作難以置信地高效。例如,`git
  50
+checkout`比`cp -a`都快,而且項目範圍的delta壓縮也比基于檔案的delta集合的做法
  51
+好多了。
  52
+
  53
+=== 初始克隆 ===
  54
+
  55
+The initial cost is worth paying in the long run, as most future operations will then be fast and offline. However, in some situations, it may be preferable to create a shallow clone with the `--depth` option. This is much faster, but the resulting clone has reduced functionality.
  56
+
  57
+當一個項目歷史很長後,與在其他版本系統裡的檢出代碼相比,創建一個克隆的開銷會
  58
+大的多。
  59
+
  60
+長遠來看,開始付出的代價還是值得付出的,因為大多將來的操作將由此變得很快,並
  61
+可以離線完成。然而,在一些情況下,使用`--depth`創建一個淺克隆比較划算些。這種
  62
+克隆初始化的更快,但得到克隆的功能有所削減。
  63
+
  64
+=== 不穩定的項目 ===
  65
+
  66
+變更的大小決定寫入的速度快慢是Git的設計。一般人做了小的改動就會提交新版本。這
  67
+裡一行臭蟲修改,那裡一個新功能,修改掉的註釋等等。但如果你的檔案在相鄰版本之
  68
+間存在極大的差異,那每次提交時,你的歷史記錄會以整個項目的大小增長。
  69
+
  70
+
  71
+
  72
+
  73
+
  74
+
  75
+
  76
+
  77
+
  78
+任何版本控制系統對此都束手無策,但標準的Git用戶將遭受更多,因為一般來說,歷史
  79
+記錄也會被克隆。
  80
+
  81
+應該檢查一下變更巨大的原因。或許檔案格式需要改變一下。小修改應該僅僅導致幾個
  82
+檔案的細小改動。
  83
+
  84
+或許,資料庫或備份/打包方案才是正選,而不是版本控制系統。例如,版本控制就不適
  85
+宜用來管理網絡攝像頭周期性拍下的照片。
  86
+
  87
+如果這些檔案實在需要不斷更改,他們實在需要版本控制,一個可能的辦法是以中心的
  88
+方式使用Git。可以創建淺克隆,這樣檢出的較少,也沒有項目的歷史記錄。當然,很多
  89
+Git工具就不能用了,並且修復必須以補丁的形式提交。這也許還不錯,因為似乎沒人需
  90
+要大幅度變化的不穩定檔案歷史。
  91
+
  92
+另一個例子是基于韌體的項目,使用巨大的二進制檔案形式。用戶對韌體檔案的變化歷
  93
+史沒有興趣,更新的壓縮比很低,因此韌體修訂將使倉庫無謂的變大。
  94
+
  95
+這種情況,源碼應該保存在一個Git倉庫裡,二進制檔案應該單獨保存。為了簡化問題,
  96
+應該發佈一個腳本,使用Git克隆源碼,對韌體只做同步或Git淺克隆。
  97
+
  98
+=== 全局計數器 ===
  99
+
  100
+一些中心版本控制系統維護一個正整數,當一個新提交被接受的時候這個整數就增長。Git則是通過哈希值來記錄所有變更,這在大多數情況下都工作的不錯。
  101
+
  102
+但一些人喜歡使用整數的方法。幸運的是,很容易就可以寫個腳本,這樣每次更新,中心Git倉庫就增大這個整數,或使用tag的方式,把最新提交的哈希值與這個整數關聯起來。
  103
+
  104
+每個克隆都可以維護這麼個計數器,但這或許沒什麼用,因為只有中心倉庫以及它的計數器對每個人才有意義。
  105
+
  106
+=== 空子目錄 ===
  107
+
  108
+空子目錄不可加入管理。可以通過創建一個空檔案以繞過這個問題。
  109
+
  110
+Git的當前實現,而不是它的設計,是造成這個缺陷的原因。如果運氣好,一旦Git得到
  111
+更多關注,更多用戶要求這個功能,這個功能就會被實現。
  112
+
  113
+=== 初始提交 ===
  114
+
  115
+傳統的計算機系統從0計數,而不是1。不幸的是,關於提交,Git並不遵從這一約定。很
  116
+多命令在初始提交之前都不友好。另外,一些極少數的情況必須作特別地處理。例如重
  117
+訂一個使用不同初始提交的分支。
  118
+
  119
+Git將從定義零提交中受益:一旦一個倉庫被創建起來,HEAD將被設為包含20個零位元組
  120
+的字元串。這個特別的提交代表一棵空的樹,沒有父節點,早于所有Git倉庫。
  121
+
  122
+然後運行git log,比如,通知用戶至今還沒有提交過變更,而不是報告致命錯誤並退出。
  123
+這與其他工具類似。
  124
+
  125
+每個初始提交都隱式地成為這個零提交的後代。
  126
+
  127
+不幸的是還有更糟糕的情況。如果把幾個具有不同初始提交的分支合併到一起,之後的
  128
+重新修訂不可避免的需要人員的介入。
  129
+
  130
+=== 介面怪癖 ===
  131
+
  132
+對提交A和提交B,表達式“A..B”和“A...B”的含義,取決於命令期望兩個終點還是一
  133
+個範圍。參見 *git help diff* 和 *git help rev-parse* 。
226  zh_tw/grandmaster.txt
... ...
@@ -0,0 +1,226 @@
  1
+== Git大師技 ==
  2
+
  3
+到現在,你應該有能力查閲 *git help* 頁,並理解几乎所有東西。然而,查明解決特
  4
+定問題需要的確切命令可能是乏味的。或許我可以省你點功夫:以下是我過去曾經需要
  5
+的一些食譜。
  6
+
  7
+=== 源碼發佈 ===
  8
+
  9
+就我的項目而言,Git完全跟蹤了我想打包並發佈給用戶的檔案。創建一個源碼包,我運
  10
+行:
  11
+
  12
+ $ git archive --format=tar --prefix=proj-1.2.3/ HEAD
  13
+
  14
+=== 提交變更  ===
  15
+
  16
+對特定項目而言,告訴Git你增加,刪除和重命名了一些檔案很麻煩。而鍵入如下命令會容易的多:
  17
+
  18
+ $ git add .
  19
+ $ git add -u
  20
+
  21
+Git將查找當前目錄的檔案並自己算出具體的情況。除了用第二個add命令,如果你也打
  22
+算這時提交,可以運行`git commit -a`。關於如何指定應被忽略的檔案,參見 *git
  23
+help ignore* 。
  24
+
  25
+你也可以用一行命令完成以上任務:
  26
+
  27
+ $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove
  28
+
  29
+這裡 *-z* 和 *-0* 選項可以消除包含特殊字元的檔案名引起的不良副作用。注意這個
  30
+命令也添加應被忽略的檔案,這時你可能需要加上 `-x` 或 `-X` 選項。
  31
+
  32
+=== 我的提交太大了! ===
  33
+
  34
+是不是忽視提交太久了?痴迷地編碼,直到現在才想起有源碼控制工具這回事?提交一
  35
+系列不相關的變更,因為那是你的風格?
  36
+
  37
+別擔心,運行:
  38
+
  39
+ $ git add -p
  40
+
  41
+為你做的每次修改,Git將展示給你變動的代碼,並詢問該變動是否應是下一次提交的一
  42
+部分。回答“y”或者“n”。也有其他選項,比如延遲決定;鍵入“?”來學習更多。
  43
+
  44
+一旦你滿意,鍵入
  45
+
  46
+ $ git commit
  47
+
  48
+來精確地提交你所選擇的變更(階段變更)。確信你沒加上 *-a* 選項,否則Git將提交
  49
+所有修改。
  50
+
  51
+如果你修改了許多地方的許多檔案怎麼辦?一個一個地查看變更令人沮喪,心態麻木。
  52
+這種情況下,使用 *git add -i* , 它的界面不是很直觀,但更靈活。敲幾個鍵,你可
  53
+以一次決定階段或非階段性提交幾個檔案,或查看並只選擇特定檔案的變更。作為另一
  54
+種選擇,你還可以運行 *git commit --interactive* ,這個命令會在你操作完後自動
  55
+進行提交。
  56
+
  57
+=== 索引:Git的中轉區域 ===
  58
+
  59
+當目前為止,我們已經忽略Git著名的'索引‘概念,但現在我們必須面對它,以解釋上
  60
+面發生的。索引是一個臨時中轉區。Git很少在你的項目和它的歷史之間直接倒騰數據。
  61
+通常,Git先寫數據到索引,然後拷貝索引中的數據到最終目的地。
  62
+
  63
+例如, *commit -a* 實際上是一個兩步過程。第一步把每個追蹤檔案當前狀態的快照放
  64
+到索引中。第二步永久記錄索引中的快照。 沒有 *-a* 的提交只執行第二步,並且只在
  65
+運行不知何故改變索引的命令才有意義,比如 *git add* 。
  66
+
  67
+通常我們可以忽略索引並假裝從歷史中直接讀並直接寫。在這個情況下,我們希望更好
  68
+地控制,因此我們操作索引。我們放我們變更的一些的快照到索引中,而不是所有的,
  69
+然後永久地記錄這個小心操縱的快照。
  70
+
  71
+=== 別丟了你的HEAD ===
  72
+
  73
+HEAD好似一個游標,通常指向最新提交,隨最新提交向前移動。一些Git命令讓你來移動
  74
+它。 例如:
  75
+
  76
+ $ git reset HEAD~3
  77
+
  78
+將立即向回移動HEAD三個提交。這樣所有Git命令都表現得好似你沒有做那最後三個提交,
  79
+然而你的檔案保持在現在的狀態。具體應用參見幫助頁。
  80
+
  81
+但如何回到將來呢?過去的提交對將來一無所知。
  82
+
  83
+如果你有原先Head的SHA1值,那麼:
  84
+
  85
+ $ git reset 1b6d
  86
+
  87
+但假設你從來沒有記下呢?別擔心,像這些命令,Git保存原先的Head為一個叫
  88
+ORGI_HEAD的標記,你可以安全體面的返回:
  89
+
  90
+ $ git reset ORIG_HEAD
  91
+
  92
+=== HEAD捕獵 ===
  93
+
  94
+或許ORG_HEAD不夠。或許你剛認識到你犯了個歷史性的錯誤,你需要回到一個早已忘記
  95
+分支上一個遠古的提交。
  96
+
  97
+預設,Git保存一個提交至少兩星期,即使你命令Git摧毀該提交所在的分支。難點是找
  98
+到相應的哈希值。你可以查看在.git/objects裡所有的哈希值並嘗試找到你期望的。但
  99
+有一個更容易的辦法。
  100
+
  101
+Git把算出的提交哈希值記錄在“.git/logs”。這個子目錄引用包括所有分支上所有活
  102
+動的歷史,同時檔案HEAD顯示它曾經有過的所有哈希值。後者可用來發現分支上一些不
  103
+小心丟掉提交的哈希值。
  104
+
  105
+The reflog command provides a friendly interface to these log files. Try
  106
+
  107
+命令reflog為訪問這些日誌檔案提供友好的介面,試試
  108
+
  109
+  $ git reflog
  110
+
  111
+而不是從reflog拷貝粘貼哈希值,試一下:
  112
+
  113
+ $ git checkout "@{10 minutes ago}"
  114
+
  115
+或者撿出後五次訪問過的提交,通過:
  116
+
  117
+ $ git checkout "@{5}"
  118
+
  119
+更多內容參見 *git help rev-parse* 的``Specifying Revisions''部分。
  120
+
  121
+你或許期望去為已刪除的提交設置一個更長的保存周期。例如:
  122
+
  123
+  $ git config gc.pruneexpire "30 days"
  124
+
  125
+意思是一個被刪除的提交會在刪除30天後,且運行 *git gc* 以後,被永久丟棄。
  126
+
  127
+你或許還想關掉 *git gc* 的自動運行:
  128
+
  129
+  $ git config gc.auto 0
  130
+
  131
+在這種情況下提交將只在你手工運行 *git gc* 的情況下才永久刪除。
  132
+
  133
+=== 基于Git構建 ===
  134
+
  135
+依照真正的UNIX風格設計,Git允許其易於用作其他程序的底層組件,比如圖形界面,
  136
+Web界面,可選擇的命令行界面,補丁管理工具,導入和轉換工具等等。實際上,一些
  137
+Git命令它們自己就是站在巨人肩膀上的腳本。通過一點修補,你可以定製Git適應你的
  138
+偏好。
  139
+
  140
+一個簡單的技巧是,用Git內建alias命令來縮短你最常使用命令:
  141
+
  142
+  $ git config --global alias.co checkout
  143
+  $ git config --global --get-regexp alias  # 顯示當前別名
  144
+  alias.co checkout
  145
+  $ git co foo                              # 和“git checkout foo”一樣
  146
+
  147
+另一個技巧,在提示符或窗口標題上打印當前分支。調用:
  148
+
  149
+  $ git symbolic-ref HEAD
  150
+
  151
+顯示當前分支名。在實際應用中,你可能最想去掉“refs/heads/”並忽略錯誤:
  152
+
  153
+  $ git symbolic-ref HEAD 2> /dev/null | cut -b 12-
  154
+
  155
+子目錄 +contrib+ 是一個基于Git工具的寶庫。它們中的一些時時會被提升為官方命令。
  156
+在Debian和Ubuntu,這個目錄位於 +/usr/share/doc/git-core/contrib+ 。
  157
+
  158
+一個受歡迎的居民是 +workdir/git-new-workdir+ 。通過聰明的符號連結,這個腳本創
  159
+建一個新的工作目錄,其歷史與原來的倉庫共享:
  160
+
  161
+  $ git-new-workdir an/existing/repo new/directory
  162
+
  163
+這個新的目錄和其中的檔案可被視為一個克隆,除了既然歷史是共享的,兩者的樹自動
  164
+保持同步。不必合併,推入或拉出。
  165
+
  166
+=== 大膽的特技 ===
  167
+
  168
+這些天,Git使得用戶意外摧毀數據變得更困難。但如若你知道你在做什麼,你可以突破
  169
+為通用命令所設的防衛保障。
  170
+
  171
+*Checkout*:未提交的變更會導致撿出失敗。銷毀你的變更,並無論如何都checkout一
  172
+ 個指定的提交,使用強制標記:
  173
+
  174
+  $ git checkout -f HEAD^
  175
+
  176
+另外,如果你為撿出指定特別路徑,那就沒有安全檢查了。提供的路徑將被不加提示地
  177
+覆蓋。如你使用這種方式的檢出,要小心。
  178
+
  179
+*Reset*: 如有未提交變更重置也會失敗。強制其通過,運行:
  180
+
  181
+  $ git reset --hard 1b6d
  182
+
  183
+*Branch*: 引起變更丟失的分支刪除會失敗。強制刪除,鍵入:
  184
+
  185
+  $ git branch -D dead_branch  # instead of -d
  186
+
  187
+類似,通過移動試圖覆蓋分支,如果隨之而來有數據丟失,也會失敗。強制移動分支,鍵入:
  188
+
  189
+  $ git branch -M source target  # 而不是 -m
  190
+
  191
+不像checkout和重置,這兩個命令延遲數據銷毀。這個變更仍然存儲在.git的子目錄裡,
  192
+並且可以通過恢復.git/logs裡的相應哈希值獲取(參見上面 上面“HEAD獵捕”)。默
  193
+認情況下,這些數據會保存至少兩星期。
  194
+
  195
+*Clean*: 一些Git命令拒絶執行,因為它們擔心會重裝未納入管理的檔案。如果你確信
  196
+ 所有未納入管理的檔案都是消耗,那就無情地刪除它們,使用:
  197
+
  198
+  $ git clean -f -d
  199
+
  200
+下次,那個討厭的命令就會工作!
  201
+
  202
+=== 阻止壞提交 ===
  203
+
  204
+愚蠢的錯誤污染我的倉庫。最可怕的是由於忘記 *git add* 而引起的檔案丟失。較小
  205
+的罪過是行末追加空格並引起合併衝突:儘管危害少,我希望浙西永遠不要出現在公開
  206
+記錄裡。
  207
+
  208
+不過我購買了傻瓜保險,通過使用一個_鈎子_來提醒我這些問題:
  209
+
  210
+ $ cd .git/hooks
  211
+ $ cp pre-commit.sample pre-commit  # 對舊版本Git,先運行chmod +x
  212
+
  213
+現在Git放棄提交,如果檢測到無用的空格或未解決的合併衝突。
  214
+
  215
+對本文檔,我最終添加以下到 *pre-commit* 鈎子的前面,來防止缺魂兒的事:
  216
+
  217
+ if git ls-files -o | grep '\.txt$'; then
  218
+   echo FAIL! Untracked .txt files.
  219
+   exit 1
  220
+ fi
  221
+
  222
+幾個git操作支持鈎子;參見 *git help hooks* 。我們早先激活了作為例子的
  223
+*post-update* 鈎子,當討論基于HTTP的Git的時候。無論head何時移動,這個鈎子都會
  224
+運行。例子腳本post-update更新Git在基于Git並不知曉的傳輸協議,諸如HTTP,通訊時
  225
+所需的檔案。
  226
+
224  zh_tw/history.txt
... ...
@@ -0,0 +1,224 @@
  1
+== 關於歷史 ==
  2
+
  3
+Git分散式本性使得歷史可以輕易編輯。但你若篡改過去,需要小心:只重寫你獨自擁有
  4
+的那部分。正如民族間會無休止的爭論誰犯下了什麼暴行一樣,如果在另一個人的克隆
  5
+裡,歷史版本與你的不同,當你們的樹互操作時,你會遇到一致性方面的問題。
  6
+
  7
+一些開發人員強烈地感覺歷史應該永遠不變,不好的部分也不變所有都不變。另一些覺
  8
+得代碼樹在向外發佈之前,應該整得漂漂亮亮的。Git同時支持兩者的觀點。像克隆,分
  9
+支和合併一樣,重寫歷史只是Git給你的另一強大功能,至于如何明智地使用它,那是你
  10
+的事了。
  11
+
  12
+=== 我認錯 ===
  13
+
  14
+Did you just commit, but wish you had typed a different message? Then run:
  15
+剛提交,但你期望你輸入的是一條不同的信息?那麼鍵入:
  16
+
  17
+ $ git commit --amend
  18
+
  19
+來改變上一條信息。意識到你還忘記了加一個檔案?運行git add來加,然後運行上面的
  20
+命令。
  21
+
  22
+希望在上次提交裡包括多一點的改動?那麼就做這些改動並運行:
  23
+
  24
+ $ git commit --amend -a
  25
+
  26
+=== 更複雜情況 ===
  27
+
  28
+假設前面的問題還要糟糕十倍。在漫長的時間裡我們提交了一堆。但你不太喜歡他們的
  29
+組織方式,而且一些提交信息需要重寫。那麼鍵入:
  30
+
  31
+ $ git rebase -i HEAD~10
  32
+
  33
+並且後10個提交會出現在你喜愛的$EDITOR。一個例子:
  34
+
  35
+    pick 5c6eb73 Added repo.or.cz link
  36
+    pick a311a64 Reordered analogies in "Work How You Want"
  37
+    pick 100834f Added push target to Makefile
  38
+
  39
+之後:
  40
+
  41
+- 通過刪除行來移去提交。
  42
+- 通過為行重新排序行來重新排序提交。
  43
+- 替換 `pick` 使用:
  44
+   * `edit` 標記一個提交需要修訂。
  45
+   * `reword` 改變日誌信息。
  46
+   * `squash` 將一個提交與其和前一個合併。
  47
+   * `fixup` 將一個提交與其和前一個合併,並丟棄日誌信息。
  48
+
  49
+保存退出。如果你把一個提交標記為可編輯,那麼運行
  50
+
  51
+ $ git commit --amend
  52
+
  53
+否則,運行:
  54
+
  55
+ $ git rebase --continue
  56
+
  57
+這樣儘早提交,經常提交:你之後還可以用rebase來規整。
  58
+
  59
+=== 本地變更之後 ===
  60
+
  61
+你正在一個活躍的項目上工作。隨着時間推移,你做了幾個本地提交,然後你使用合併
  62
+與官方版本同步。在你準備好提交到中心分支之前,這個循環會重複幾次。
  63
+
  64
+但現在你本地Git克隆摻雜了你的改動和官方改動。你更期望在變更列表裡,你所有的變
  65
+更能夠連續。
  66
+
  67
+這就是上面提到的 *git rebase* 所做的工作。在很多情況下你可以使用 *--onto* 標
  68
+記以避免交互。
  69
+
  70
+另外參見 *git help rebase* 以獲取這個讓人驚奇的命令更詳細的例子。你可以拆分提
  71
+交。你甚至可以重新組織一棵樹的分支。
  72
+
  73
+=== 重寫歷史 ===
  74
+
  75
+偶爾,你需要做一些代碼控制,好比從正式的照片中去除一些人一樣,需要從歷史記錄
  76
+裡面徹底的抹掉他們。例如,假設我們要發佈一個項目,但由於一些原因,項目中的某
  77
+個檔案不能公開。或許我把我的信用卡號記錄在了一個文本檔案裡,而我又意外的把它
  78
+加入到了這個項目中。僅僅刪除這個檔案是不夠的,因為從別的提交記錄中還是可以訪
  79
+問到這個檔案。因此我們必須從所有的提交記錄中徹底刪除這個檔案。
  80
+
  81
+ $ git filter-branch --tree-filter 'rm top/secret/file' HEAD
  82
+
  83
+參見 *git help filter-branch* ,那裡討論了這個例子並給出一個更快的方法。一般
  84
+地, *filter-branch* 允許你使用一個單一命令來大範圍地更改歷史。
  85
+
  86
+此後,+.git/refs/original+目錄描述操作之前的狀態。檢查命令filter-branch的確做
  87
+了你想要做的,然後刪除此目錄,如果你想運行多次filter-branch命令。
  88
+
  89
+最後,用你修訂過的版本替換你的項目克隆,如果你想之後和它們交互的話。
  90
+
  91
+=== 製造歷史 ===
  92
+
  93
+[[makinghistory]]
  94
+想把一個項目遷移到Git嗎?如果這個項目是在用比較有名氣的系統,那可以使用一些其
  95
+他人已經寫好的腳本,把整個項目歷史記錄導出來放到Git裡。
  96
+
  97
+否則,查一下 *git fast-import* ,這個命令會從一個特定格式的文本讀入,從頭來創
  98
+建Git歷史記錄。通常可以用這個命令很快寫一個腳本運行一次,一次遷移整個項目。
  99
+
  100
+作為一個例子,粘貼以下所列到臨時檔案,比如/tmp/history:
  101
+
  102
+----------------------------------
  103
+commit refs/heads/master
  104
+committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000
  105
+data <<EOT
  106
+Initial commit.
  107
+EOT
  108
+
  109
+M 100644 inline hello.c
  110
+data <<EOT
  111
+#include <stdio.h>
  112
+
  113
+int main() {
  114
+  printf("Hello, world!\n");
  115
+  return 0;
  116
+}
  117
+EOT
  118
+
  119
+
  120
+commit refs/heads/master
  121
+committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800
  122
+data <<EOT
  123
+Replace printf() with write().
  124
+EOT
  125
+
  126
+M 100644 inline hello.c
  127
+data <<EOT
  128
+#include <unistd.h>
  129
+
  130
+int main() {
  131
+  write(1, "Hello, world!\n", 14);
  132
+  return 0;
  133
+}
  134
+EOT
  135
+
  136
+----------------------------------
  137
+
  138
+之後從這個臨時檔案創建一個Git倉庫,鍵入:
  139
+
  140
+ $ mkdir project; cd project; git init
  141
+ $ git fast-import --date-format=rfc2822 < /tmp/history
  142
+
  143
+你可以從這個項目checkout出最新的版本,使用:
  144
+
  145
+ $ git checkout master .
  146
+
  147
+命令*git fast-export* 轉換任意倉庫到 *git fast-import* 格式,你可以研究其輸
  148
+出來寫導出程序, 也以可讀格式傳送倉庫。的確,這些命令可以發送倉庫文本檔案
  149
+通過只接受文本的渠道。
  150
+
  151
+
  152
+=== 哪兒錯了? ===
  153
+
  154
+你剛剛發現程序裡有一個功能出錯了,而你十分確定幾個月以前它運行的很正常。天啊!
  155
+這個臭蟲是從哪裡冒出來的?要是那時候能按照開發的內容進行過測試該多好啊。
  156
+
  157
+現在說這個已經太晚了。然而,即使你過去經常提交變更,Git還是可以精確的找出問題所在:
  158
+
  159
+ $ git bisect start
  160
+ $ git bisect bad HEAD
  161
+ $ git bisect good 1b6d
  162
+
  163
+Git從歷史記錄中檢出一個中間的狀態。在這個狀態上測試功能,如果還是有問題:
  164
+
  165
+ $ git bisect bad
  166
+
  167
+如果可以工作了,則把"bad"替換成"good"。Git會再次幫你找到一個以確定的好版本和
  168
+壞版本之間的狀態,通過這種方式縮小範圍。經過一系列的迭代,這種二分搜索會幫你
  169
+找到導致這個錯誤的那次提交。一旦完成了問題定位的調查,你可以返回到原始狀態,
  170
+鍵入:
  171
+
  172
+ $ git bisect reset
  173
+
  174
+不需要手工測試每一次改動,執行如下命令可以自動的完成上面的搜索:
  175
+
  176
+ $ git bisect run my_script
  177
+
  178
+Git使用指定命令(通常是一個一次性的腳本)的返回值來決定一次改動是否是正確的:
  179
+命令退出時的代碼0代表改動是正確的,125代表要跳過對這次改動的檢查,1到127之間
  180
+的其他數值代表改動是錯誤的。返回負數將會中斷整個bisect的檢查。
  181
+
  182
+你還能做更多的事情: 幫助文檔解釋了如何展示bisects, 檢查或重放bisect的日誌,並
  183
+可以通過排除對已知正確改動的檢查,得到更好的搜索速度。
  184
+
  185
+=== 誰讓事情變糟了? ===
  186
+
  187
+和其他許多版本控制系統一樣,Git也有一個"blame"命令:
  188
+
  189
+ $ git blame bug.c
  190
+
  191
+這個命令可以標註出一個指定的檔案裡每一行內容的最後修改者,和最後修改時間。但
  192
+不像其他版本控制系統,Git的這個操作是在綫下完成的,它只需要從本地磁碟讀取信息。
  193
+
  194
+=== 個人經驗 ===
  195
+
  196
+在一個中心版本控制系統裡,歷史的更改是一個困難的操作,並且只有管理員才有權這
  197
+麼做。沒有網絡,克隆,分支和合併都沒法做。像一些基本的操作如瀏覽歷史,或提交
  198
+變更也是如此。在一些系統裡,用戶使用網絡連接僅僅是為了查看他們自己的變更,或
  199
+打開檔案進行編輯。
  200
+
  201
+中心繫統排斥離線工作,也需要更昂貴的網絡設施,特別是當開發人員增多的時候。最
  202
+重要的是,所有操作都一定程度變慢,一般在用戶避免使用那些能不用則不用的高級命
  203
+令時。在極端的情況下,即使是最基本的命令也會變慢。當用戶必須運行緩慢的命令的
  204
+時候,由於工作流被打斷,生產力降低。
  205
+
  206
+我有這些的一手經驗。Git是我使用的第一個版本控制系統。我很快學會適應了它,用了
  207
+它提供的許多功能。我簡單地假設其他系統也是相似的:選擇一個版本控制系統應該和
  208
+選擇一個編輯器或瀏覽器沒啥兩樣。
  209
+
  210
+在我之後被迫使用中心繫統的時候,我被震驚了。我那有些脆弱的網絡沒給Git帶來大麻
  211
+煩,但是當它需要像本地硬碟一樣穩定的時候,它使開發困難重重。另外,我發現我自
  212
+己有選擇地避免特定的命令,以避免踏雷,這極大地影響了我,使我不能按照我喜歡的
  213
+方式工作。
  214
+
  215
+當我不得不運行一個慢的命令的時候,這種等待極大地破壞了我思緒連續性。在等待服
  216
+務器通訊完成的時候,我選擇做其他的事情以度過這段時光,比如查看郵件或寫其他的
  217
+文檔。當我返回我原先的工作場景的時候,這個命令早已結束,並且我還需要浪費時間
  218
+試圖記起我之前正在做什麼。人類不擅長場景間的切換。
  219
+
  220
+還有一個有意思的大眾悲劇效應:預料到網絡擁擠,為了減少將來的等待時間,每個人
  221
+將比以往消費更多的頻寬在各種操作上。共同的努力加劇了擁擠,這等於是鼓勵個人下
  222
+次消費更多頻寬以避免更長時間的等待。
  223
+
  224
+
96  zh_tw/intro.txt
... ...
@@ -0,0 +1,96 @@
  1
+== 入門 ==
  2
+
  3
+我將用類比方式來介紹版本控制的概念。更嚴謹的解釋參見
  4
+http://en.wikipedia.org/wiki/Revision_control[維基百科版本修訂控制條目]。
  5
+
  6
+=== 工作是玩 ===
  7
+
  8
+我几乎玩了一輩子電腦遊戲。相反,我成人之後才開始使用版本控制系統。也許不止我
  9
+一個人這樣,對比兩者工作方式可使概念更易解釋,也易於理解。
  10
+
  11
+把編寫代碼或編輯文檔與玩遊戲類比。一旦有很多進展,你會喜歡存檔。去做這你會點
  12
+擊你的編輯器的存檔按鈕。
  13
+
  14
+但這將覆蓋老版本。就像那些學校裡玩的老遊戲,只有一個存檔:你確實可以保存,但
  15
+你不能回到之前的狀態了。這真讓人掃興,可能之前狀態恰好保存了這個遊戲特別有意
  16
+思一關,也許某天你想再玩一下。或者更糟糕的,你當前的存檔是個必敗的局,這樣你
  17
+就不得不從頭開始玩了。
  18
+
  19
+=== 版本控制 ===
  20
+
  21
+在編輯的時候,如果想保留舊版本,你可以將檔案“另存為”一個不同的檔案,或在保
  22
+存之前將檔案拷貝到別處。你會把壓縮這些檔案節省空間。這是一個原始的勞動密集型
  23
+的版本控制方式。遊戲軟件早就提高了這塊,很多都提供多個基于時間戳的自動存檔。
  24
+
  25
+讓我們看看稍稍複雜的情況。比如你有很多放在一起的檔案,比如項目的原始碼,或網
  26
+站的檔案。現在如果你想保留舊版本你不得存檔整個目錄。手工保存多個版本很不方便,
  27
+很快會耗費巨大。
  28
+
  29
+一些電腦遊戲的存檔真的包含在一個充滿檔案的目錄裡。這些遊戲為玩家屏蔽了具體細
  30
+節,展現一個方便易用的界面來管理該目錄的不同版本。
  31
+
  32
+版本控制系統也沒有兩樣。兩者都有友好的界面來管理目錄裡的東西。你可以頻繁保存,
  33
+也可以之後加載任一保存。不像大多計算機遊戲,版本控制系統通常精於節省存儲空間。
  34
+一般情況如果兩個版本間只有少數檔案的變更,每個檔案的變更也不大,那就只存儲差
  35
+異的部分以節省存儲空間,而不是把全部拷貝的都保存下來。
  36
+
  37
+=== 分佈控制 ===
  38
+
  39
+現在設想有一個很難的遊戲。太難打了,以至于世界各地很多骨灰級玩家決定組隊,分
  40
+享他們遊戲存檔以攻克它。Speedrun們就是實際中的例子:在同一個遊戲裡,玩家們分別
  41
+攻克不同的等級,協同工作以創造驚人戰績。
  42
+
  43
+你如何搭建一個系統,使得他們易於得到彼此的所保存的?並易於上載新的?
  44
+
  45
+在過去,每個項目都使用中心式版本控制。某個伺服器上放所有保存的遊戲記錄。其他
  46
+人都不用了。每個玩家在他們機器上最多保留幾個遊戲記錄。當一個玩家想更新進度時
  47
+候,他們需要把最新進度從主伺服器下載下來,玩一會兒,保存並上載到主伺服器以供
  48
+其他人使用。
  49
+
  50
+假如一個玩家由於某種原因,想得到一個較舊版本的遊戲進度怎麼樣?或許當前保存的
  51
+遊戲是一個注定的敗局,因為某人在第三級忘記撿某個物品;他們希望能找到最近一個
  52
+可以完成的遊戲記錄。或者他們想比較兩個舊版本間的差異,來估算某個特定玩家幹了
  53
+多少活。
  54
+
  55
+查看舊版本的理由有很多,但檢查的辦法都是一樣的。他們必須去問中心伺服器要那個
  56
+舊版本的記錄。需要的舊版本越多,和伺服器的交互就越多。
  57
+
  58
+新一代的版本控制系統,Git就是其中之一,是分散式的,可以被認作廣義上的中心式系
  59
+統。從主伺服器下載時玩家會得到所有保存的記錄,而不僅是最新版。這看起來他們好
  60
+像把中心伺服器做了個鏡像。
  61
+
  62
+最初的克隆操作可能比較費時,特別當有很長歷史的時,但從長遠看這是值得的。一個
  63
+顯而易見的好處是,當查看一個舊版本時,不再需要和中心伺服器通訊了。
  64
+
  65
+=== 一個誤區 ===
  66
+
  67
+一個很常見的錯誤觀念是,分散式系統不適合需要官方中心倉庫的項目。這與事實並
  68
+不相符。給誰照相也不會偷走他們的靈魂。類似地,克隆主倉庫並不降低它的重要性。
  69
+
  70
+一般來說,一個中心版本控制系統能做的任何事,一個良好設計的分散式系統都能做得
  71
+更好。網絡資源總要比本地資源耗費更費。不過我們應該在稍後分析分散式方案的缺點,
  72
+這樣人們才不會按照習慣做出錯誤的比較。
  73
+
  74
+一個小項目或許只需要分散式系統提供的一小部分功能,但是,在你的項目很小的時候,
  75
+說你應該用規劃不好的系統,好比說,在計算較小數目的時候應該使用羅馬數字嗎?
  76
+
  77
+而且,你的項目的增長可能會超出你最初的預期。從一開始就使用Git好似帶著一把瑞士
  78
+軍刀,儘管你很多時候只是用它來開開瓶蓋。到你迫切需要一把改錐的那一天,你就會
  79
+慶幸你有的不單單是一個啟瓶器。
  80
+
  81
+=== 合併衝突 ===
  82
+
  83
+對於這個話題,電腦遊戲的類比顯得太單薄了。那麼讓我們再來看看文檔編輯的情況吧。
  84
+
  85
+假設Alice在文檔開頭插入一行,並且Bob在文檔末尾添加一行。他們都上傳了他們的改
  86
+動。大多數系統將自動給出一個合理的處理方式:接受且合併他們的改動,這樣Alice和
  87
+Bob兩人的改動都會生效。
  88
+
  89
+現在假設Alice和Bob對檔案的同一行做了不同的改動。如果沒有人工參與的話,這個沖
  90
+突是無法解決的。第二個人在上載檔案時,會收到 _合併衝突_ 的通知,要麼用一個人的
  91
+改動覆蓋另一個的,要麼完全修訂這一行。
  92
+
  93
+更複雜的情況也可能出現。版本控制系統自己處理相對簡單的情況,把困難的情況留給
  94
+人來處理。它們的行為通常是可配置的。
  95
+
  96
+
208  zh_tw/multiplayer.txt
... ...
@@ -0,0 +1,208 @@
  1
+== 多人Git ==
  2
+
  3
+我最初在一個私人項目上使用Git,那裡我是唯一的開發。在與Git分散式本性有關的命
  4
+令中,我只用到了 *pull* 和 *clone*,用以在不同地方保持同一個項目。
  5
+
  6
+後來我想用Git發佈我的代碼,並且包括其他貢獻者的變更。我不得不學習如何管理有來
  7
+自世界各地的多個開發的項目,幸運的是,這是Git的長處,也可以說是其存在的理由。
  8
+
  9
+=== 我是誰? ===
  10
+
  11
+每個提交都有一個作者姓名和電子信箱,這顯示在 *git log* 裡。預設, Git使用系統
  12
+設定來填充這些域。要顯示地設定,鍵入:
  13
+
  14
+  $ git config --global user.name "John Doe"
  15
+  $ git config --global user.email johndoe@example.com
  16
+
  17