gitmagic-20160304/0000755000175000017500000000000012666307504013000 5ustar sbadiasbadiagitmagic-20160304/.gitignore0000644000175000017500000000007712666307504014774 0ustar sbadiasbadia*.log *.fo *.aux *.out *.pdf fop-zh* /book* /conf /fop/* .* *~ gitmagic-20160304/zh_tw/0000755000175000017500000000000012666307504014133 5ustar sbadiasbadiagitmagic-20160304/zh_tw/drawbacks.txt0000644000175000017500000001523112666307504016637 0ustar sbadiasbadia== 附錄 A: Git的缺點 == 有一些Git的問題,我已經藏在毯子下面了。有些可以通過腳本或回調方法輕易地解決, 有些需要重組或重定義項目,少數剩下的煩惱,還只能等待。或者更好地,投入進來幫 忙。 === SHA1 的弱點 === 隨着時間的推移,密碼學家發現越來越多的SHA1的弱點。已經發現對對資源雄厚的組織 哈希衝撞是可能的。在幾年內,或許甚至一個一般的PC也將有足夠計算能力悄悄摧毀一 個Git倉庫。 希望在進一步研究摧毀SHA1之前,Git能遷移到一個更好的哈希算法。 === 微軟 Windows === Git在微軟Windows上可能有些繁瑣: - http://cygwin.com/[Cygwin] ,, 一個Windows下的類Linux的環境,包含一個 http://cygwin.com/packages/git/[ 一個Git在Windows下的移植]. - http://code.google.com/p/msysgit/[基于MSys的Git] 是另一個,要求最小運行時支持,不過一些命令不能馬上工作。 === 不相關的檔案 === 如果你的項目非常大,包含很多不相關的檔案,而且正在不斷改變,Git可能比其他系統 更不管用,因為獨立的檔案是不被跟蹤的。Git跟蹤整個項目的變更,這通常才是有益的。 一個方案是將你的項目拆成小塊,每個都由相關檔案組成。如果你仍然希望在同一個資 源庫裡保存所有內容的話,可以使用 *git submodule* 。 === 誰在編輯什麼? === 一些版本控制系統在編輯前強迫你顯示地用某個方法標記一個檔案。儘管這種要求很煩 人,尤其是需要和中心伺服器通訊時,不過它還是有以下兩個好處的: 1. 比較速度快,因為只有被標記的檔案需要檢查。 2. 可以知道誰在這個檔案上工作,通過查詢在中心伺服器誰把這個檔案標記為編輯狀 態。 使用適當的腳本,你也可以使Git達到同樣的效果。這要求程序員協同工作,當他編輯一 個檔案的時候還要運行特定的腳本。 === 檔案歷史 === 因為Git記錄的是項目範圍的變更,重造單一檔案的變更歷史比其他跟蹤單一檔案的版本 控制系統要稍微麻煩些。 好在麻煩還不大,也是值得的,因為Git其他的操作難以置信地高效。例如,`git checkout`比`cp -a`都快,而且項目範圍的delta壓縮也比基于檔案的delta集合的做法 好多了。 === 初始克隆 === 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. 當一個項目歷史很長後,與在其他版本系統裡的檢出代碼相比,創建一個克隆的開銷會 大的多。 長遠來看,開始付出的代價還是值得付出的,因為大多將來的操作將由此變得很快,並 可以離線完成。然而,在一些情況下,使用`--depth`創建一個淺克隆比較划算些。這種 克隆初始化的更快,但得到克隆的功能有所削減。 === 不穩定的項目 === 變更的大小決定寫入的速度快慢是Git的設計。一般人做了小的改動就會提交新版本。這 裡一行臭蟲修改,那裡一個新功能,修改掉的註釋等等。但如果你的檔案在相鄰版本之 間存在極大的差異,那每次提交時,你的歷史記錄會以整個項目的大小增長。 任何版本控制系統對此都束手無策,但標準的Git用戶將遭受更多,因為一般來說,歷史 記錄也會被克隆。 應該檢查一下變更巨大的原因。或許檔案格式需要改變一下。小修改應該僅僅導致幾個 檔案的細小改動。 或許,資料庫或備份/打包方案才是正選,而不是版本控制系統。例如,版本控制就不適 宜用來管理網絡攝像頭周期性拍下的照片。 如果這些檔案實在需要不斷更改,他們實在需要版本控制,一個可能的辦法是以中心的 方式使用Git。可以創建淺克隆,這樣檢出的較少,也沒有項目的歷史記錄。當然,很多 Git工具就不能用了,並且修復必須以補丁的形式提交。這也許還不錯,因為似乎沒人需 要大幅度變化的不穩定檔案歷史。 另一個例子是基于韌體的項目,使用巨大的二進制檔案形式。用戶對韌體檔案的變化歷 史沒有興趣,更新的壓縮比很低,因此韌體修訂將使倉庫無謂的變大。 這種情況,源碼應該保存在一個Git倉庫裡,二進制檔案應該單獨保存。為了簡化問題, 應該發佈一個腳本,使用Git克隆源碼,對韌體只做同步或Git淺克隆。 === 全局計數器 === 一些中心版本控制系統維護一個正整數,當一個新提交被接受的時候這個整數就增長。Git則是通過哈希值來記錄所有變更,這在大多數情況下都工作的不錯。 但一些人喜歡使用整數的方法。幸運的是,很容易就可以寫個腳本,這樣每次更新,中心Git倉庫就增大這個整數,或使用tag的方式,把最新提交的哈希值與這個整數關聯起來。 每個克隆都可以維護這麼個計數器,但這或許沒什麼用,因為只有中心倉庫以及它的計數器對每個人才有意義。 === 空子目錄 === 空子目錄不可加入管理。可以通過創建一個空檔案以繞過這個問題。 Git的當前實現,而不是它的設計,是造成這個缺陷的原因。如果運氣好,一旦Git得到 更多關注,更多用戶要求這個功能,這個功能就會被實現。 === 初始提交 === 傳統的計算機系統從0計數,而不是1。不幸的是,關於提交,Git並不遵從這一約定。很 多命令在初始提交之前都不友好。另外,一些極少數的情況必須作特別地處理。例如重 訂一個使用不同初始提交的分支。 Git將從定義零提交中受益:一旦一個倉庫被創建起來,HEAD將被設為包含20個零位元組 的字元串。這個特別的提交代表一棵空的樹,沒有父節點,早于所有Git倉庫。 然後運行git log,比如,通知用戶至今還沒有提交過變更,而不是報告致命錯誤並退出。 這與其他工具類似。 每個初始提交都隱式地成為這個零提交的後代。 不幸的是還有更糟糕的情況。如果把幾個具有不同初始提交的分支合併到一起,之後的 重新修訂不可避免的需要人員的介入。 === 介面怪癖 === 對提交A和提交B,表達式“A..B”和“A...B”的含義,取決於命令期望兩個終點還是一 個範圍。參見 *git help diff* 和 *git help rev-parse* 。 gitmagic-20160304/zh_tw/branch.txt0000644000175000017500000002460312666307504016136 0ustar sbadiasbadia== 分支巫術 == 即時分支合併是Git最給力的殺手鐧。 *問題* :外部因素要求必須切換場景。在發佈版本中突然蹦出個嚴重缺陷。某個特性完 成的截至日期就要來臨。在項目關鍵部分可以提供幫助的一個開發正打算離職。所有情 況逼迫你停下所有手頭工作,全力撲到到這個完全不同的任務上。 打斷思維的連續性會使你的生產力大大降低,並且切換上下文也更麻煩,更大的損失。 使用中心版本控制我們必須從中心伺服器下載一個新的工作拷貝。分散式系統的情況就 好多了,因為我們能夠在本地克隆所需要的版本。 但是克隆仍然需要拷貝整個工作目錄,還有直到給定點的整個歷史記錄。儘管Git使用文 件共享和硬連結減少了花費,項目檔案自身還是必須在新的工作目錄裡重建。 *方案* :Git有一個更好的工具對付這種情況,比克隆快多了而且節省空間: *git branch* 。 使用這個魔咒,目錄裡的檔案突然從一個版本變到另一個。除了只是在歷史記錄裡上跳 下竄外,這個轉換還可以做更多。你的檔案可以從上一個發佈版變到實驗版本到當前開 發版本到你朋友的版本等等。 === 老闆鍵 === 曾經玩過那樣的遊戲嗎?按一個鍵(“老闆鍵”),屏幕立即顯示一個電子表格或別的? 那麼如果老闆走進辦公室,而你正在玩遊戲,就可以快速將遊戲藏起來。 在某個目錄: $ echo "I'm smarter than my boss" > myfile.txt $ git init $ git add . $ git commit -m "Initial commit" 我們已經創建了一個Git倉庫,該倉庫記錄一個包含特定信息的檔案。現在我們鍵入: $ git checkout -b boss # 之後似乎沒啥變化 $ echo "My boss is smarter than me" > myfile.txt $ git commit -a -m "Another commit" 看起來我們剛剛只是覆蓋了原來的檔案並提交了它。但這是個錯覺。鍵入: $ git checkout master # 切到檔案的原先版本 嘿真快!這個檔案就恢復了。並且如果老闆決定窺視這個目錄,鍵入: $ git checkout boss # 切到適合老闆看的版本 你可以在兩個版本之間相切多少次就切多少次,而且每個版本都可以獨立提交。 === 骯髒的工作 === [[branch]] 比如你正在開發某個特性,並且由於某種原因,你需要回退三個版本,臨時加進幾行打 印語句來,來看看一些東西是如何工作的。那麼: $ git commit -a $ git checkout HEAD~3 現在你可以到處加醜陋的臨時代碼。你甚至可以提交這些改動。當你做完的時候, $ git checkout master 來返回到你原來的工作。看,所有未提交變更都結轉了。 如果你後來想保存臨時變更怎麼辦?簡單: $ git checkout -b dirty 只要在切換到主分支之前提交就可以了。無論你什麼時候想回到髒的變更,只需鍵入: $ git checkout dirty 我們在前面章節討論加載舊狀態的時候,曾經接觸過這個命令。最終我們把故事說全: 檔案改變成請求的狀態,但我們必須離開主分支。從現在開始的任何提交都會將你的文 件提交到另一條不同的路,這個路可以之後命名。 換一個說法,在checkout一個舊狀態之後,Git自動把你放到一個新的,未命名的分支, 這個分支可以使用 *git checkout -b* 來命名和保存。 === 快速修訂 === 你正在做某件事的當間,被告知先停所有的事情,去修理一個新近發現的臭蟲,這個臭 蟲在提交 `1b6d...`: $ git commit -a $ git checkout -b fixes 1b6d 那麼一旦你修正了這個臭蟲: $ git commit -a -m "Bug fixed" $ git checkout master 並可以繼續你原來的任務。你甚至可以“合併”到最新修訂: $ git merge fixes === 合併 === 一些版本控制系統,創建分支很容易,但把分支合併回來很難。使用Git,合併簡直是家 常便飯,以至于甚至你可能對其發生沒有察覺。 我們很久之前就遇到合併了。 *pull* 命令取出提交併合並它們到你的當前分支。如果 你沒有本地變更,那這個合併就是一個“快進”,相當於中心式版本控制系統裡的一個 弱化的獲取最新版本操作。但如有本地變更,Git將自動合併,並報告任何衝突。 通常,一個提交只有一個“父提交”,也叫前一個提交。合併分支到一起產生一個至少 有兩個父的提交。這就引出了問題: `HEAD~10` 真正指哪個提交?一個提交可能有多個 父,那我們跟哪個呢? 原來這個表示每次選擇第一個父。這是可取的,因為在合併時候當前分支成了第一個父; 多數情況下我們只關注我們在當前分支都改了什麼,而不是從其他分支合併來的變更。 你可以用插入符號來特別指定父。比如,顯示來自第二個父的日誌: $ git log HEAD^2 你可以忽略數字以指代第一個父。比如,顯示與第一個父的差別: $ git diff HEAD^ 你可以結合其他類型使用這個記號。比如: $ git checkout 1b6d^^2~10 -b ancient 開始一個新分支 ``ancient'' ,表示第一個父的第二個父的倒數第十次提交的狀態。 === 不間斷工作流 === 經常在硬件項目裡,計劃的第二步必須等第一步完成才能開始。待修的汽車傻等在車庫 裡,直到特定的零件從工廠運來。一個原型在其可以構建之前,可能苦等晶片成型。 軟件項目可能也類似。新功能的第二部分不得不等待,直到第一部分發佈並通過測試。 一些項目要求你的代碼需要審批才能接受,因此你可能需要等待第一部分得到批准,才 能開始第二部分。 多虧了無痛分支合併,我們可以不必遵循這些規則,在第一部分正式準備好前開始第二 部分的工作。假設你已經將第一部分提交並發去審批,比如說你現在在主分支。那麼分 岔: $ git checkout -b part2 接下來,做第二部分,隨時可以提交變更。只要是人就可能犯錯誤,經常你將回到第一 部分在修修補補。如果你非常幸運,或者超級棒,你可能不必做這幾行: $ git checkout master # 回到第一部分 $ 修復問題 $ git commit -a # 提交變更 $ git checkout part2 # 回到第二部分 $ git merge master # 合併這些改動 最終,第一部分獲得批准: $ git checkout master # 回到第一部分 $ submit files # 對世界發佈 $ git merge part2 # 合併第二部分 $ git branch -d part2 # 刪除分支“part2” 現在你再次處在主分支,第二部分的代碼也在工作目錄。 很容易擴展這個技巧,應用到任意數目的部分。它也很容易追溯分支:假如你很晚才意 識到你本應在7次提交前就創建分支。那麼鍵入: $ git branch -m master part2 # 重命名“master”分支為“part2”。 $ git branch master HEAD~7 # 以七次前提交建一個新的“master”。 分支 `master` 只有第一部分內容,其他內容在分支 `part2` 。 我們現在後一個分支; 我們創建了 `master` 分支還沒有切換過去,因為我們想繼續工作在 `part2` 。這是不 尋常的。直到現在,我們已經在創建之後切換到分支,如: $ git checkout HEAD~7 -b master # 創建分支,並切換過去。 === 重組雜亂 === 或許你喜歡在同一個分支下完成工作的方方面面。你想為自己保留工作進度並希望其他 人只能看到你仔細整理過後的提交。開啟一對分支: $ git branch sanitized # 為乾淨提交創建分支 $ git checkout -b medley # 創建並切換分支以進去工作 接下來,做任何事情:修臭蟲,加特性,加臨時代碼,諸如此類,經常按這種方式提交。 然後: $ git checkout sanitized $ git cherry-pick medley^^ 應用分支 ``medley'' 的祖父提交到分支 ``sanitized'' 。通過合適的挑選(像選櫻桃 那樣)你可以構建一個只包含成熟代碼的分支,而且相關的提交也組織在一起。 === 管理分支 === 列出所有分支: $ git branch 預設你從叫 ``master'' 的分支開始。一些人主張別碰“master”分支,而是創建你自 己版本的新分支。 選項 *-d* 和 *-m* 允許你來刪除和移動(重命名)分支。參見 *git help branch* 。 分支``master'' 是一個有用的慣例。其他人可能假定你的倉庫有一個叫這個名字的分 支,並且該分支包含你項目的官方版本。儘管你可以重命名或抹殺 ``master'' 分支, 你最好還是尊重這個約定。 === 臨時分支 === 很快你會發現你經常會因為一些相似的原因創建短期的分支:每個其它分支只是為了保 存當前狀態,那樣你就可以直接跳到較老狀態以修復高優先順序的臭蟲之類。 可以和電視的換台做類比,臨時切到別的頻道,來看看其它台那正放什麼。但並不是簡 單地按幾個按鈕,你不得不創建,檢出,合併,以及刪除臨時分支。幸運的是,Git已經 有了和電視機遙控器一樣方便的快捷方式: $ git stash 這個命令保存當前狀態到一個臨時的地方(一個隱藏的地方)並且恢復之前狀態。你的 工作目錄看起來和你開始編輯之前一樣,並且你可以修復臭蟲,引入之前變更等。當你 想回到隱藏狀態的時候,鍵入: $ git stash apply # 你可能需要解決一些衝突 你可以有多個隱藏,並用不同的方式來操作他們。參見 *git help slash* 。也許你已 經猜到,Git維護在這個場景之後的分支以執行魔法技巧. === 按你希望的方式工作 === 你可能猶疑于分支是否值得一試。畢竟,克隆也几乎一樣快,並且你可以用 *cd* 來在 彼此之間切換,而不是用Git深奧的命令。 考慮一下瀏覽器。為什麼同時支持多標籤和多窗口?因為允許兩者同時接納納了多種風 格的用戶。一些用戶喜歡只保持一個打開的窗口,然後用標籤瀏覽多個網頁。一些可能 堅持另一個極端:任何地方都沒有標籤的多窗口。一些喜好處在兩者之間。 分支類似你工作目錄的標籤,克隆類似打開的瀏覽器新窗口。這些是本地操作很快,那 為什麼不試着找出最適合你的組合呢?Git讓你按你確實所希望的那樣工作。 gitmagic-20160304/zh_tw/secrets.txt0000644000175000017500000002651512666307504016355 0ustar sbadiasbadia== 揭開面紗 == 我們揭開Git神秘面紗,往裡瞧瞧它是如何創造奇蹟的。我會跳過細節。更深入的描述參 見 http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[ 用戶手 冊]。 === 大象無形 === Git怎麼這麼謙遜寡言呢?除了偶爾提交和合併外,你可以如常工作,就像不知道版本控 制系統存在一樣。那就是,直到你需要它的時候,而且那是你歡欣的時候,Git一直默默 注視着你。 其他版本控制系統強迫你與繁文縟節和官僚主義不斷鬥爭。檔案的權限可能是隻讀的, 除非你顯式地告訴中心伺服器哪些檔案你打算編輯。即使最基本的命令,隨着用戶數目 的增多,也會慢的像爬一樣。中心伺服器可能正跟蹤什麼人,什麼時候check out了什麼 代碼。當網絡連接斷了的時候,你就遭殃了。開發人員不斷地與這些版本控制系統的種 種限製作鬥爭。一旦網絡或中心伺服器癱瘓,工作就嘎然而止。 與之相反,Git簡單地在你工作目錄下的`.git`目錄保存你項目的歷史。這是你自己的歷 史拷貝,因此你可以保持離線,直到你想和他人溝通為止。你擁有你的檔案命運完全的 控制權,因為Git可以輕易在任何時候從`.git`重建一個保存狀態。 === 數據完整性 === 很多人把加密和保持信息機密關聯起來,但一個同等重要的目標是保證信息安全。合理 使用哈希加密功能可以防止無意或有意的數據損壞行為。 一個SHA1哈希值可被認為是一個唯一的160位ID數,用它可以唯一標識你一生中遇到的每 個位元組串。 實際上不止如此:每個位元組串可供任何人用好多輩子。 對一個檔案而言,其整體內容的哈希值可以被看作這個檔案的唯一標識ID數。 因為一個SHA1哈希值本身也是一個位元組串,我們可以哈希包括其他哈希值的位元組串。這 個簡單的觀察出奇地有用:查看“哈希鏈”。我們之後會看Git如何利用這一點來高效地 保證數據完整性。 簡言之,Git把你數據保存在`.git/objects`子目錄,那裡看不到正常檔案名,相反你只 看到ID。通過用ID作為檔案名,加上一些檔案鎖和時間戳技巧,Git把任意一個原始的文 件系統轉化為一個高效而穩定的資料庫。 === 智能 === Git是如何知道你重命名了一個檔案,即使你從來沒有明確提及這個事實?當然,你或許 是運行了 *git mv* ,但這個命令和 *git add* 緊接 *git rm* 是完全一樣的。 Git啟發式地找出相連版本之間的重命名和拷貝。實際上,它能檢測檔案之間代碼塊的移 動或拷貝!儘管它不能覆蓋所有的情況,但它已經做的很好了,並且這個功能也總在改 進中。如果它在你那兒不工作的話,可以嘗試打開開銷更高的拷貝檢測選項,並考慮升 級。 === 索引 === 為每個加入管理的檔案,Git在一個名為“index”的檔案裡記錄統計信息,諸如大小, 創建時間和最後修改時間。為了確定檔案是否更改,Git比較其當前統計信息與那些在索 引裡的統計信息。如果一致,那Git就跳過重新讀檔案。 因為統計信息的調用比讀檔案內容快的很多,如果你僅僅編輯了少數幾個檔案,Git几乎 不需要什麼時間就能更新他們的統計信息。 我們前面講過索引是一個中轉區。為什麼一堆檔案的統計數據是一個中轉區?因為添加 命令將檔案放到Git的資料庫並更新它們的統計信息,而無參數的提交命令創建一個提交, 只基于這些統計信息和已經在資料庫裡的檔案。 === Git的源起 === 這個 http://lkml.org/lkml/2005/4/6/121[ Linux內核郵件列表帖子] 描述了導致Git 的一系列事件。整個討論線索是一個令人着迷的歷史探究過程,對Git史學家而言。 === 對象資料庫 === 你數據的每個版本都保存在“對象資料庫”裡,其位於子目錄`.git/objects`;其他位 于`.git/`的較少數據:索引,分支名,標籤,配置選項,日誌,頭提交的當前位置等。 對象資料庫樸素而優雅,是Git的力量之源。 `.git/objects`裡的每個檔案是一個對象。有3中對象跟我們有關:“blob”對象, “tree”對象,和“commit”對象。 === Blob對象 === 首先來一個小把戲。去一個檔案名,任意檔案名。在一個空目錄: $ echo sweet > YOUR_FILENAME $ git init $ git add . $ find .git/objects -type f 你將看到 +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+ 。 我如何在不知道檔案名的情況下知道這個?這是因為以下內容的SHA1哈希值: "blob" SP "6" NUL "sweet" LF 是 aa823728ea7d592acc69b36875a482cdf3fd5c8d,這裡SP是一個空格,NUL是一個0位元組, LF是一個換行符。你可以驗證這一點,鍵入: $ printf "blob 6\000sweet\n" | sha1sum Git基于“內容定址”:檔案並不按它們的檔案名存儲,而是按它們包含內容的哈希值, 在一個叫“blob對象”的檔案裡。我們可以把檔案內容的哈希值看作一個唯一ID,這樣 在某種意義上我們通過他們內容放置檔案。開始的“blob 6”只是一個包含對象類型與 其長度的頭;它簡化了內部存儲。 這樣我可以輕易語言你所看到的。檔案名是無關的:只有裡面的內容被用作構建blob對象。 你可能想知道對相同的檔案什麼會發生。試圖加一個你檔案的拷貝,什麼檔案名都行。 在 +.git/objects+ 的內容保持不變,不管你加了多少。Git只存儲一次數據。 順便說一句,在 +.git/objects+ 裡的檔案用zlib壓縮,因此你不應該直接查看他們。 可以通過http://www.zlib.net/zpipe.c[zpipe -d] 管道, 或者鍵入: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d 這漂亮地打印出給定的對象。 === Tree對象 === 但檔案名在哪?它們必定在某個階段保存在某個地方。Git在提交時得到檔案名: $ git commit # 輸入一些信息。 $ find .git/objects -type f 你應看到3個對象。這次我不能告訴你這兩個新檔案是什麼,因為它部分依賴你選擇的文 件名。我繼續進行,假設你選了``rose''。如果你沒有,你可以重寫歷史以讓它看起來 像似你做了: $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose' $ find .git/objects -type f 現在你硬看到檔案 +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+ ,因為這是以下內容的SHA1哈希值: "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d 檢查這個檔案真的包含上面內容通過鍵入: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch 使用zpipe,驗證哈希值是容易的: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum 與查看檔案相比,哈希值驗證更技巧一些,因為其輸出不止包含原始未壓縮檔案。 這個檔案是一個“tree”對象:一組數據包含檔案類型,檔案名和哈希值。在我們的例 子裡,檔案類型是100644,這意味着“rose”是一個一般檔案,並且哈希值指blob對象, 包含“rose”的內容。其他可能檔案類型有可執行,連結或者目錄。在最後一個例子裡, 哈希值指向一個tree對象。 在一些過渡性的分支,你會有一些你不在需要的老的對象,儘管有寬限過期之後,它們 會被自動清除,現在我們還是將其刪除,以使我們比較容易跟上這個玩具例子。 $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune 在真實項目裡你通常應該避免像這樣的命令,因為你在破換備份。如果你期望一個乾淨 的倉庫,通常最好做一個新的克隆。還有,直接操作 +.git+ 時一定要小心:如果 Git命令同時也在運行會怎樣,或者突然停電?一般,引用應由 *git update-ref -d* 刪除,儘管通常手工刪除 +refs/original+ 也是安全的。 === Commit對象 === 我們已經解釋了三個對象中的兩個。第三個是“commit”對象。其內容依賴于提交信息 以及其創建的日期和時間。為滿足這裡我們所有的,我們不得不調整一下: $ git commit --amend -m Shakespeare # 改提交信息 $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Rig timestamps and authors. $ find .git/objects -type f 你現在應看到 +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ 是下列 內容的SHA1哈希值: "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice 1234567890 -0800" LF "committer Bob 1234567890 -0800" LF LF "Shakespeare" LF 和前面一樣,你可以運行zpipe或者cat-file來自己看。 這是第一個提交,因此沒有父提交,但之後的提交將總有至少一行,指定一個父提交。 === 沒那麼神 === Git的秘密似乎太簡單。看起來似乎你可以整合幾個shell腳本,加幾行C代碼來弄起來, 也就幾個小時的事:一個基本檔案操作和SHA1哈希化的混雜,用鎖檔案裝飾一下,檔案 同步保證健壯性。實際上,這準確描述了Git的最早期版本。儘管如此,除了巧妙地打包 以節省空間,巧妙地索引以省時間,我們現在知道Git如何靈巧地改造檔案系統成為一個 對版本控制完美的資料庫。 例如,如果對象資料庫裡的任何一個檔案由於硬碟錯誤損毀,那麼其哈希值將不再匹配, 這個錯誤會報告給我們。通過哈希化其他對象的哈希值,我們在所有層面維護數據完整 性。Commit對象是原子的,也就是說,一個提交永遠不會部分地記錄變更:在我們已經 存儲所有相關tree對象,blob對象和父commit對象之後,我們才可以計算提交的的哈希 值並將其存儲在資料庫,對象資料庫不受諸如停電之類的意外中斷影響。 我們打敗即使是最狡猾的對手。假設有誰試圖悄悄修改一個項目裡一個遠古版本檔案的 內容。為使對象據庫看起來健康,他們也必須修改相應blob對象的哈希值,既然它現在 是一個不同的位元組串。這意味着他們講不得不引用這個檔案的tree對象的哈希值,並反 過來改變所有與這個tree相關的commit對象的哈希值,還要加上這些提交所有後裔的哈 希值。這暗示官方head的哈希值與這個壞倉庫不同。通過跟蹤不匹配哈希值線索,我 們可以查明殘缺檔案,以及第一個被破壞的提交。 總之,只要20個位元組代表最後一次提交的是安全的,不可能篡改一個Git倉庫。 那麼Git的著名功能怎樣呢?分支?合併?標籤?單純的細節。當前head保存在檔案 +.git /HEAD+ ,其中包含了一個commit對象的哈希值。該哈希值在運行提交以及其他命 令是更新。分支几乎一樣:它們是保存在 +.git/refs/heads+ 的檔案。標籤也是:它們 住在住在 +.git/refs/tags+ ,但它們由一套不同的命令更新。 gitmagic-20160304/zh_tw/multiplayer.txt0000644000175000017500000002013612666307504017245 0ustar sbadiasbadia== 多人Git == 我最初在一個私人項目上使用Git,那裡我是唯一的開發。在與Git分散式本性有關的命 令中,我只用到了 *pull* 和 *clone*,用以在不同地方保持同一個項目。 後來我想用Git發佈我的代碼,並且包括其他貢獻者的變更。我不得不學習如何管理有來 自世界各地的多個開發的項目,幸運的是,這是Git的長處,也可以說是其存在的理由。 === 我是誰? === 每個提交都有一個作者姓名和電子信箱,這顯示在 *git log* 裡。預設, Git使用系統 設定來填充這些域。要顯示地設定,鍵入: $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com 去掉global選項設定只對當前倉庫生效。 === Git在SSH, HTTP上 === 假設你有ssh訪問權限,以訪問一個網頁伺服器,但上面並沒有安裝Git。儘管比着它的 原生協議效率低,Git也是可以通過HTTP來進行通信的。 那麼在你的帳戶下,下載,編譯並安裝Git。在你的網頁目錄裡創建一個Git倉庫: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update 對較老版本的Git,只拷貝還不夠,你應運行: $ chmod a+x hooks/post-update 現在你可以通過SSH從隨便哪個克隆發佈你的最新版本: $ git push web.server:/path/to/proj.git master 那隨便誰都可以通過如下命令得到你的項目: $ git clone http://web.server/proj.git === Git在隨便什麼上 === 想無需伺服器,甚至無需網絡連接的時候同步倉庫?需要在緊急時期湊合一下?我們 已經看過<>。 我們可以來來會會傳送這些檔案以傳輸git倉庫, 通過任何媒介,但一個更有效率的工具是 *git bundle* 。 發送者創建一個“檔案包”: $ git bundle create somefile HEAD 然後傳輸這個檔案包, +somefile+ ,給某個其他參與者:電子郵件,優盤,一個 *xxd* 打印品和一個OCR掃描器,通過電話讀位元組,狼煙,等等。接收者通過鍵入如下命 令從檔案包獲取提交: $ git pull somefile 接收者甚至可以在一個空倉庫做這個。不考慮大小, +somefile+ 可以包含整個原先 git倉庫。 在較大的項目裡,可以通過只打包其他倉庫缺少的變更消除浪費。例如,假設提交 ``1b6d...''是兩個參與者共享的最近提交: $ git bundle create somefile HEAD ^1b6d 如果做的頻繁,人可能容易忘記剛發了哪個提交。幫助頁面建議使用標籤解決這個問題。 即,在你發了一個檔案包後,鍵入: $ git tag -f lastbundle HEAD 並創建較新檔案包,使用: $ git bundle create newbundle HEAD ^lastbundle === 補丁:全球貨幣 === 補丁是變更的文本形式,易於計算機理解,人也類似。補丁可以通吃。你可以給開發電 郵一個補丁,不用管他們用的什麼版本控制系統。只要你的觀眾可以讀電子郵件,他們 就能看到你的修改。類似,在你這邊,你只需要一個電子郵件帳號:不必搭建一個在綫 的Git倉庫。 回想一下第一章: $ git diff 1b6d > my.patch 輸出是一個補丁,可以粘貼到電子郵件裡用以討論。在一個Git倉庫,鍵入: $ git apply < my.patch 來打這個補丁。 在更正式些的設置裡,當作者名字以及或許簽名應該記錄下的時候,為過去某一刻生成 補丁,鍵入: $ git format-patch 1b6d 結果檔案可以給 *git-send-email* 發送,或者手工發送。你也可以指定一個提交範圍: $ git format-patch 1b6d..HEAD^^ 在接收一端,保存郵件到一個檔案,然後鍵入: $ git am < email.txt 這就打了補丁並創建了一個提交,包含諸如作者之類的信息。 使用瀏覽器郵件客戶端,在保存補丁為檔案之前,你可能需要建一個按鈕,看看郵件內 容原來的原始形式。 對基于mbox的郵件客戶端有些微不同,但如果你在使用的話,你可能是那種能輕易找出 答案的那種人,不用讀教程。 === 對不起,移走了 === 克隆一個倉庫後,運行 *git push* 或 *git pull* 講自動推到或從原先URL拉。Git 如何做這個呢?秘密在和克隆一起創建的配置選項。讓我們看一下: $ git config --list 選項 +remote.origin.url+ 控制URL源;``origin'' 是給源倉庫的暱稱。和 ``master'' 分支的慣例一樣,我們可以改變或刪除這個暱稱,但通常沒有理由這麼做。 如果原先倉庫移走,我們可以更新URL,通過: $ git config remote.origin.url git://new.url/proj.git 選項 +branch.master.merge+ 指定 *git pull* 裡的預設遠端分支。在初始克隆的時候, 它被設為原倉庫的當前分支,因此即使原倉庫之後挪到一個不同的分支,後來的 pull也將忠實地跟着原來的分支。 這個選項只使用我們初次克隆的倉庫,它的值記錄在選項 +branch.master.remote+ 。如果我們從其他倉庫拉入,我們必須顯示指定我們想要哪個分支: $ git pull git://example.com/other.git master 以上也解釋了為什麼我們較早一些push和pull的例子沒有參數。 === 遠端分支 === 當你克隆一個倉庫,你也克隆了它的所有分支。你或許沒有注意到因為Git將它們隱藏 起來了:你必須明確地要求。這使得遠端倉庫裡的分支不至于干擾你的分支,也使 Git對初學者稍稍容易些。 列出遠端分支,使用: $ git branch -r 你應該看到類似: origin/HEAD origin/master origin/experimental 這顯示了遠端倉庫的分支和HEAD,可以用在常用的Git命令裡。例如,假設你已經做了 很多提交,並希望和最後取到的版本比較一下。你可以搜索適當的SHA1哈希值,但使用 下面命令更容易些: $ git diff origin/HEAD 或你可以看看``experimental''分支都有啥: $ git log origin/experimental === 多遠端 === 假設另兩個開發在同一個項目上工作,我們希望保持兩個標籤。我們可以同事跟多個倉庫: $ git remote add other git://example.com/some_repo.git $ git pull other some_branch 現在我們已經從第二個倉庫合併到一個分支,並且我們已容易訪問所有倉庫的所有 分支。 $ git diff origin/experimental^ other/some_branch~5 但如果為了不影響自己的工作,我們只想比較他們的變更怎麼辦呢?換句話說,我們想 檢查一下他們的分支,又不使他們的變更入侵我們的工作目錄。那不是運行pull命令, 而是運行: $ git fetch # Fetch from origin, the default. $ git fetch other # Fetch from the second programmer. 這只是獲取歷史。儘管工作目錄維持不變,我們可以參考任何倉庫的任何分支,使用 一個Git命令,因為我們現在有一個本地拷貝。 回想一下,在幕後,一個pull是簡單地一個 *fetch* 然後 *merge* 。通常,我們 *pull* 因為我們想在獲取後合併最近提交;這個情況是一個值得注意的例外。 關於如何去除遠端倉庫,如何忽略特定分支等更多,參見 *git help remote* 。 === 我的喜好 === 對我手頭的項目,我喜歡貢獻者去準備倉庫,這樣我可以從其中拉。一些Git伺服讓你 點一個按鈕,擁有自己的分叉項目。 在我獲取一個樹之後,我運行Git命令去瀏覽並檢查這些變更,理想情況下這些變更組織 良好,描述良好。我合併這些變更,也或許做些編輯。直到滿意,我才把變更推入主資 源庫。 儘管我不經常收到貢獻,我相信這個方法擴展性良好。參見 http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[ 這篇 來自Linus Torvalds的博客 ] 獃在Git的世界裡比補丁檔案稍更方便,因為不用我將補丁轉換到Git提交。更進一步, Git處理諸如作者姓名和信箱地址的細節,還有時間和日期,以及要求作者描述他們的提 交。 gitmagic-20160304/zh_tw/preface.txt0000644000175000017500000000707312666307504016310 0ustar sbadiasbadia= Git 魔法 = Ben Lynn 2007年8月 == 前言 == http://git.or.cz/[Git] 堪稱版本控制瑞士軍刀。這個可靠、多才多藝、用途多樣的校 訂工具異常靈活,以致不易掌握,更別說精通了。 正如Arthur C. Clarke所說,足夠先進的技術與魔法無二。這是學習Git的好辦法:新手 不妨忽略Git的內部機理,只當小把戲玩,借助Git其奇妙的能力,逗逗朋友,氣氣敵人。 為了不陷入細節,我們對特定功能提供大面上的講解。在反覆應用之後,慢慢地你會理 解每個小技巧如何工作,以及如何組合這些技巧以滿足你的需求。 .翻譯 - link:/\~blynn/gitmagic/intl/zh_cn/[簡體中文]: 俊傑,萌和江薇。 link:/~blynn/gitmagic/intl/zh_tw/[正體中文] 由 + cconv -f UTF8-CN -t UTF8-TW + 轉換。 - link:/~blynn/gitmagic/intl/fr/[法文]: Alexandre Garel。也在 http://tutoriels.itaapy.com/[itaapy]。 - link:/~blynn/gitmagic/intl/de/[德文]: Benjamin Bellee和Armin Stebich;也在 http://gitmagic.lordofbikes.de/[Armin的網站]。 - http://www.slideshare.net/slide_user/magia-git[葡萄牙文]: Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[ODT版]]。 - link:/~blynn/gitmagic/intl/ru/[俄文]: Tikhon Tarnavsky, Mikhail Dymskov, 和其他人。 - link:/~blynn/gitmagic/intl/es/[西班牙]: Rodrigo Toledo和Ariset Llerena Tapia。 - link:/~blynn/gitmagic/intl/vi/[越南文]: Trần Ngọc Quân; 也在 http://vnwildman.users.sourceforge.net/gitmagic.html[他的網站]. .其它版本 - link:book.html[單一檔案]: 純HTML,無CSS。 - link:book.pdf[PDF檔案]: 打印效果好. - http://packages.debian.org/gitmagic[Debian包], http:://packages.ubuntu.com/gitmagic[Ubuntu包]: 本站快速本地拷貝。如果 http://csdcf.stanford.edu/status/[下線了]會方便些。 - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[紙質書 [Amazon.com]]: 64 頁, 15.24cm x 22.86cm, 黑白。 沒有電子設備的時候會方便些。 === 致謝! === 那麼多人對本文檔的翻譯讓我受寵若驚。他們的付出拓寬了讀者群,我非常感激。 Dustin Sallings、Alberto Bertogli、James Cameron、Douglas Livingstone、 Michael Budde、Richard Albury、Tarmigan、 Derek Mahar、Frode Aannevik、 Keith Rarick、 Andy Somerville、 Ralf Recker、 Øyvind A. Holm、 Miklos Vajna、 Sébastien Hinderer、 Thomas Miedema、 Joe Malin、 和Tyler Breisacher對本文檔 正確性和優化做出了貢獻。 François Marier維護Debian包,該Debian包起初由Daniel Baumann創建。 感謝其他很多提供幫助和鼓勵的人。名單太長了我無法一一寫下。 如果我不小心把你的名字落下,請告訴我或者發一個補丁。 *免費 Git 主機*: 底下網站可以免費放置公開專案。 非常感謝底下網站贊助放置手冊。 - http://repo.or.cz/[http://repo.or.cz/] - http://gitorious.org/[http://gitorious.org/] - http://github.com/[http://github.com/] 私有項目收錢。 - http://www.assembla.com/[Assembla]: 私有項目收錢, 雖然有 1 gigabyte 空間免費. === 許可 === 本指南在http://www.gnu.org/licenses/gpl-3.0.html[ GNU通用公共許可協議版本3 ] 之下發佈。很自然,源碼保存在一個Git倉庫裡,可以通過以下命令獲得源碼: $ git clone git://repo.or.cz/gitmagic.git # 建立 "gitmagic" 目錄. 或從以下鏡像得到: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git $ git clone git://git.assembla.com/gitmagic.git gitmagic-20160304/zh_tw/basic.txt0000644000175000017500000001601412666307504015757 0ustar sbadiasbadia== 基本技巧 == 與其一頭紮進Git命令的海洋中,不如來點基本的例子試試手。它們簡單而且實用。實際 上,在開始使用Git的頭幾個月,我所用的從來沒超出本章介紹的內容。 === 保存狀態 === 要不來點猛的?在做之前,先為當前目錄所有檔案做個快照,使用: $ git init $ git add . $ git commit -m "My first backup" 現在如果你的編輯亂了套,恢復之前的版本: $ git reset --hard 再次保存狀態: $ git commit -a -m "Another backup" === 添加、刪除、重命名 === 以上命令將只跟蹤你第一次運行 *git add* 命令時就已經存在的檔案。如果要添加新文 件或子目錄,你需要告訴Git: $ git add readme.txt Documentation 類似,如果你想讓Git忘記某些檔案: $ git rm kludge.h obsolete.c $ git rm -r incriminating/evidence/ 這些檔案如果還沒刪除,Git刪除它們。 重命名檔案和先刪除舊檔案,再添加新檔案的一樣。也有一個快捷方式 *git mv* ,和 *mv* 命令的用法一樣。例如: $ git mv bug.c feature.c === 進階撤銷/重做 === 有時候你只想把某個時間點之後的所有改動都回滾掉,因為這些的改動是不正確的。那 麼: $ git log 來顯示最近提交列表,以及他們的SHA1哈希值: ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob Date: Tue Mar 14 01:59:26 2000 -0800 Replace printf() with write(). commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alice Date: Thu Jan 1 00:00:00 1970 +0000 Initial commit. ---------------------------------- 哈希值的前幾個字元足夠確定一個提交;也可以拷貝粘貼完整的哈希值,鍵入: $ git reset --hard 766f 來恢復到一個指定的提交狀態,並從記錄裡永久抹掉所有比該記錄新一些的提交。 另一些時候你想簡單地跳到一個舊狀態。這種情況,鍵入: $ git checkout 82f5 這個操作將把你帶回過去,同時也保留較新提交。然而,像科幻電影裡時光旅行一樣, 如果你這時編輯並提交的話,你將身處另一個現實裡,因為你的動作與開始時相比是不 同的。 這另一個現實叫作“分支”(branch),之後 <>。 至于現在,只要記住: $ git checkout master 會把你帶到當下來就可以了。另外,為避免Git的抱怨,應該在每次運行checkout之前提 交(commit)或重置(reset)你的改動。 還以電腦遊戲作為類比: - *`git reset --hard`*: 加載一個舊記錄並刪除所有比之新的記錄。 - *`git checkout`*: 加載一個舊記錄,但如果你在這個記錄上玩,遊戲狀態將偏離第 一輪的較新狀態。你現在打的所有遊戲記錄會在你剛進入的、代表另一個真實的分支 裡。<>。 你可以選擇只恢復特定檔案和目錄,通過將其加在命令之後: $ git checkout 82f5 some.file another.file 小心,這種形式的 *checkout* 會不聲不響地覆蓋檔案。為阻止意外發生,在運行任何 checkout命令之前做提交,尤其在初學Git的時候。通常,任何時候你覺得對運行某個命 令不放心,無論Git命令還是不是Git命令,就先運行一下 *git commit -a* 。 不喜歡拷貝站題哈希值?那就用: $ git checkout :/"My first b" 來跳到以特定字元串開頭的提交。你也可以回到倒數第五個保存狀態: $ git checkout master~5 === 撤銷 === 在法庭上,事件可以從法庭記錄裡敲出來。同樣,你可以檢出特定提交以撤銷。 $ git commit -a $ git revert 1b6d 講撤銷給定哈希值的提交。本撤銷被記錄為一個新的提交,你可以通過運行 *git log* 來確認這一點。 === 變更日誌生成 === 一些項目要求生成變更日誌http://en.wikipedia.org/wiki/Changelog[changelog]. 生 成一個,通過鍵入: $ git log > ChangeLog === 下載檔案 === 得到一個由Git管理的項目的拷貝,通過鍵入: $ git clone git://server/path/to/files 例如,得到我用來創建該站的所有檔案: $ git clone git://git.or.cz/gitmagic.git 我們很快會對 *clone* 命令談的很多。 === 到最新 === 如果你已經使用 *git clone* 命令得到了一個項目的一份拷貝,你可以更新到最新版, 通過: $ git pull === 快速發佈 === 假設你寫了一個腳本,想和他人分享。你可以只告訴他們從你的計算機下載,但如果此 時你正在改進你的腳本,或加入試驗性質的改動,他們下載了你的腳本,他們可能由此 陷入困境。當然,這就是發佈周期存在的原因。開發人員可能頻繁進行項目修改,但他 們只在他們覺得代碼可以見人的時候才擇時發佈。 用Git來完成這項,需要進入你的腳本所在目錄: $ git init $ git add . $ git commit -m "First release" 然後告訴你的用戶去運行: $ git clone your.computer:/path/to/script 來下載你的腳本。這要假定他們有ssh訪問權限。如果沒有,需要運行 *git daemon* 並 告訴你的用戶去運行: $ git clone git://your.computer/path/to/script 從現在開始,每次你的腳本準備好發佈時,就運行: $ git commit -a -m "Next release" 並且你的用戶可以通過進入包含你腳本的目錄,並鍵入下列命令,來更新他們的版本: $ git pull 你的用戶永遠也不會取到你不想讓他們看到的腳本版本。顯然這個技巧對所有的東西都 是可以,不僅是對腳本。 === 我們已經做了什麼? === 找出自從上次提交之後你已經做了什麼改變: $ git diff 或者自昨天的改變: $ git diff "@{yesterday}" 或者一個特定版本與倒數第二個變更之間: $ git diff 1b6d "master~2" 輸出結果都是補丁格式,可以用 *git apply* 來把補丁打上。也可以試一下: $ git whatchanged --since="2 weeks ago" 我也經常用http://sourceforge.net/projects/qgit[qgit] 瀏覽歷史, 因為他的圖形界 面很養眼,或者 http://jonas.nitro.dk/tig/[tig] ,一個文本界面的東西,很慢的網 絡狀況下也工作的很好。也可以安裝web 伺服器,運行 *git instaweb* ,就可以用任 何瀏覽器瀏覽了。 === 練習 === 比方A,B,C,D是四個連續的提交,其中B與A一樣,除了一些檔案刪除了。我們想把這 些刪除的檔案加回D。我們如何做到這個呢? 至少有三個解決方案。假設我們在D: 1. A與B的差別是那些刪除的檔案。我們可以創建一個補丁代表這些差別,然後吧補丁 打上: $ git diff B A | git apply 2. 既然這些檔案存在A,我們可以把它們拿出來: $ git checkout A foo.c bar.h 3. 我們可以把從A到B的變化視為可撤銷的變更: $ git revert B 哪個選擇最好?這取決於你的喜好。利用Git滿足自己需求是容易,經常還有多個方法。 gitmagic-20160304/zh_tw/grandmaster.txt0000644000175000017500000002233612666307504017211 0ustar sbadiasbadia== Git大師技 == 到現在,你應該有能力查閲 *git help* 頁,並理解几乎所有東西。然而,查明解決特 定問題需要的確切命令可能是乏味的。或許我可以省你點功夫:以下是我過去曾經需要 的一些食譜。 === 源碼發佈 === 就我的項目而言,Git完全跟蹤了我想打包並發佈給用戶的檔案。創建一個源碼包,我運 行: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === 提交變更 === 對特定項目而言,告訴Git你增加,刪除和重命名了一些檔案很麻煩。而鍵入如下命令會容易的多: $ git add . $ git add -u Git將查找當前目錄的檔案並自己算出具體的情況。除了用第二個add命令,如果你也打 算這時提交,可以運行`git commit -a`。關於如何指定應被忽略的檔案,參見 *git help ignore* 。 你也可以用一行命令完成以上任務: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove 這裡 *-z* 和 *-0* 選項可以消除包含特殊字元的檔案名引起的不良副作用。注意這個 命令也添加應被忽略的檔案,這時你可能需要加上 `-x` 或 `-X` 選項。 === 我的提交太大了! === 是不是忽視提交太久了?痴迷地編碼,直到現在才想起有源碼控制工具這回事?提交一 系列不相關的變更,因為那是你的風格? 別擔心,運行: $ git add -p 為你做的每次修改,Git將展示給你變動的代碼,並詢問該變動是否應是下一次提交的一 部分。回答“y”或者“n”。也有其他選項,比如延遲決定;鍵入“?”來學習更多。 一旦你滿意,鍵入 $ git commit 來精確地提交你所選擇的變更(階段變更)。確信你沒加上 *-a* 選項,否則Git將提交 所有修改。 如果你修改了許多地方的許多檔案怎麼辦?一個一個地查看變更令人沮喪,心態麻木。 這種情況下,使用 *git add -i* , 它的界面不是很直觀,但更靈活。敲幾個鍵,你可 以一次決定階段或非階段性提交幾個檔案,或查看並只選擇特定檔案的變更。作為另一 種選擇,你還可以運行 *git commit --interactive* ,這個命令會在你操作完後自動 進行提交。 === 索引:Git的中轉區域 === 當目前為止,我們已經忽略Git著名的'索引‘概念,但現在我們必須面對它,以解釋上 面發生的。索引是一個臨時中轉區。Git很少在你的項目和它的歷史之間直接倒騰數據。 通常,Git先寫數據到索引,然後拷貝索引中的數據到最終目的地。 例如, *commit -a* 實際上是一個兩步過程。第一步把每個追蹤檔案當前狀態的快照放 到索引中。第二步永久記錄索引中的快照。 沒有 *-a* 的提交只執行第二步,並且只在 運行不知何故改變索引的命令才有意義,比如 *git add* 。 通常我們可以忽略索引並假裝從歷史中直接讀並直接寫。在這個情況下,我們希望更好 地控制,因此我們操作索引。我們放我們變更的一些的快照到索引中,而不是所有的, 然後永久地記錄這個小心操縱的快照。 === 別丟了你的HEAD === HEAD好似一個游標,通常指向最新提交,隨最新提交向前移動。一些Git命令讓你來移動 它。 例如: $ git reset HEAD~3 將立即向回移動HEAD三個提交。這樣所有Git命令都表現得好似你沒有做那最後三個提交, 然而你的檔案保持在現在的狀態。具體應用參見幫助頁。 但如何回到將來呢?過去的提交對將來一無所知。 如果你有原先Head的SHA1值,那麼: $ git reset 1b6d 但假設你從來沒有記下呢?別擔心,像這些命令,Git保存原先的Head為一個叫 ORGI_HEAD的標記,你可以安全體面的返回: $ git reset ORIG_HEAD === HEAD捕獵 === 或許ORG_HEAD不夠。或許你剛認識到你犯了個歷史性的錯誤,你需要回到一個早已忘記 分支上一個遠古的提交。 預設,Git保存一個提交至少兩星期,即使你命令Git摧毀該提交所在的分支。難點是找 到相應的哈希值。你可以查看在.git/objects裡所有的哈希值並嘗試找到你期望的。但 有一個更容易的辦法。 Git把算出的提交哈希值記錄在“.git/logs”。這個子目錄引用包括所有分支上所有活 動的歷史,同時檔案HEAD顯示它曾經有過的所有哈希值。後者可用來發現分支上一些不 小心丟掉提交的哈希值。 The reflog command provides a friendly interface to these log files. Try 命令reflog為訪問這些日誌檔案提供友好的介面,試試 $ git reflog 而不是從reflog拷貝粘貼哈希值,試一下: $ git checkout "@{10 minutes ago}" 或者撿出後五次訪問過的提交,通過: $ git checkout "@{5}" 更多內容參見 *git help rev-parse* 的``Specifying Revisions''部分。 你或許期望去為已刪除的提交設置一個更長的保存周期。例如: $ git config gc.pruneexpire "30 days" 意思是一個被刪除的提交會在刪除30天後,且運行 *git gc* 以後,被永久丟棄。 你或許還想關掉 *git gc* 的自動運行: $ git config gc.auto 0 在這種情況下提交將只在你手工運行 *git gc* 的情況下才永久刪除。 === 基于Git構建 === 依照真正的UNIX風格設計,Git允許其易於用作其他程序的底層組件,比如圖形界面, Web界面,可選擇的命令行界面,補丁管理工具,導入和轉換工具等等。實際上,一些 Git命令它們自己就是站在巨人肩膀上的腳本。通過一點修補,你可以定製Git適應你的 偏好。 一個簡單的技巧是,用Git內建alias命令來縮短你最常使用命令: $ git config --global alias.co checkout $ git config --global --get-regexp alias # 顯示當前別名 alias.co checkout $ git co foo # 和“git checkout foo”一樣 另一個技巧,在提示符或窗口標題上打印當前分支。調用: $ git symbolic-ref HEAD 顯示當前分支名。在實際應用中,你可能最想去掉“refs/heads/”並忽略錯誤: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- 子目錄 +contrib+ 是一個基于Git工具的寶庫。它們中的一些時時會被提升為官方命令。 在Debian和Ubuntu,這個目錄位於 +/usr/share/doc/git-core/contrib+ 。 一個受歡迎的居民是 +workdir/git-new-workdir+ 。通過聰明的符號連結,這個腳本創 建一個新的工作目錄,其歷史與原來的倉庫共享: $ git-new-workdir an/existing/repo new/directory 這個新的目錄和其中的檔案可被視為一個克隆,除了既然歷史是共享的,兩者的樹自動 保持同步。不必合併,推入或拉出。 === 大膽的特技 === 這些天,Git使得用戶意外摧毀數據變得更困難。但如若你知道你在做什麼,你可以突破 為通用命令所設的防衛保障。 *Checkout*:未提交的變更會導致撿出失敗。銷毀你的變更,並無論如何都checkout一 個指定的提交,使用強制標記: $ git checkout -f HEAD^ 另外,如果你為撿出指定特別路徑,那就沒有安全檢查了。提供的路徑將被不加提示地 覆蓋。如你使用這種方式的檢出,要小心。 *Reset*: 如有未提交變更重置也會失敗。強制其通過,運行: $ git reset --hard 1b6d *Branch*: 引起變更丟失的分支刪除會失敗。強制刪除,鍵入: $ git branch -D dead_branch # instead of -d 類似,通過移動試圖覆蓋分支,如果隨之而來有數據丟失,也會失敗。強制移動分支,鍵入: $ git branch -M source target # 而不是 -m 不像checkout和重置,這兩個命令延遲數據銷毀。這個變更仍然存儲在.git的子目錄裡, 並且可以通過恢復.git/logs裡的相應哈希值獲取(參見上面 上面“HEAD獵捕”)。默 認情況下,這些數據會保存至少兩星期。 *Clean*: 一些Git命令拒絶執行,因為它們擔心會重裝未納入管理的檔案。如果你確信 所有未納入管理的檔案都是消耗,那就無情地刪除它們,使用: $ git clean -f -d 下次,那個討厭的命令就會工作! === 阻止壞提交 === 愚蠢的錯誤污染我的倉庫。最可怕的是由於忘記 *git add* 而引起的檔案丟失。較小 的罪過是行末追加空格並引起合併衝突:儘管危害少,我希望浙西永遠不要出現在公開 記錄裡。 不過我購買了傻瓜保險,通過使用一個_鈎子_來提醒我這些問題: $ cd .git/hooks $ cp pre-commit.sample pre-commit # 對舊版本Git,先運行chmod +x 現在Git放棄提交,如果檢測到無用的空格或未解決的合併衝突。 對本文檔,我最終添加以下到 *pre-commit* 鈎子的前面,來防止缺魂兒的事: if git ls-files -o | grep '\.txt$'; then echo FAIL! Untracked .txt files. exit 1 fi 幾個git操作支持鈎子;參見 *git help hooks* 。我們早先激活了作為例子的 *post-update* 鈎子,當討論基于HTTP的Git的時候。無論head何時移動,這個鈎子都會 運行。例子腳本post-update更新Git在基于Git並不知曉的傳輸協議,諸如HTTP,通訊時 所需的檔案。 gitmagic-20160304/zh_tw/history.txt0000644000175000017500000002255612666307504016407 0ustar sbadiasbadia== 關於歷史 == Git分散式本性使得歷史可以輕易編輯。但你若篡改過去,需要小心:只重寫你獨自擁有 的那部分。正如民族間會無休止的爭論誰犯下了什麼暴行一樣,如果在另一個人的克隆 裡,歷史版本與你的不同,當你們的樹互操作時,你會遇到一致性方面的問題。 一些開發人員強烈地感覺歷史應該永遠不變,不好的部分也不變所有都不變。另一些覺 得代碼樹在向外發佈之前,應該整得漂漂亮亮的。Git同時支持兩者的觀點。像克隆,分 支和合併一樣,重寫歷史只是Git給你的另一強大功能,至于如何明智地使用它,那是你 的事了。 === 我認錯 === 剛提交,但你期望你輸入的是一條不同的信息?那麼鍵入: $ git commit --amend 來改變上一條信息。意識到你還忘記了加一個檔案?運行git add來加,然後運行上面的 命令。 希望在上次提交裡包括多一點的改動?那麼就做這些改動並運行: $ git commit --amend -a === 更複雜情況 === 假設前面的問題還要糟糕十倍。在漫長的時間裡我們提交了一堆。但你不太喜歡他們的 組織方式,而且一些提交信息需要重寫。那麼鍵入: $ git rebase -i HEAD~10 並且後10個提交會出現在你喜愛的$EDITOR。一個例子: pick 5c6eb73 Added repo.or.cz link pick a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile 之後: - 通過刪除行來移去提交。 - 通過為行重新排序行來重新排序提交。 - 替換 `pick` 使用: * `edit` 標記一個提交需要修訂。 * `reword` 改變日誌信息。 * `squash` 將一個提交與其和前一個合併。 * `fixup` 將一個提交與其和前一個合併,並丟棄日誌信息。 保存退出。如果你把一個提交標記為可編輯,那麼運行 $ git commit --amend 否則,運行: $ git rebase --continue 這樣儘早提交,經常提交:你之後還可以用rebase來規整。 === 本地變更之後 === 你正在一個活躍的項目上工作。隨着時間推移,你做了幾個本地提交,然後你使用合併 與官方版本同步。在你準備好提交到中心分支之前,這個循環會重複幾次。 但現在你本地Git克隆摻雜了你的改動和官方改動。你更期望在變更列表裡,你所有的變 更能夠連續。 這就是上面提到的 *git rebase* 所做的工作。在很多情況下你可以使用 *--onto* 標 記以避免交互。 另外參見 *git help rebase* 以獲取這個讓人驚奇的命令更詳細的例子。你可以拆分提 交。你甚至可以重新組織一棵樹的分支。 === 重寫歷史 === 偶爾,你需要做一些代碼控制,好比從正式的照片中去除一些人一樣,需要從歷史記錄 裡面徹底的抹掉他們。例如,假設我們要發佈一個項目,但由於一些原因,項目中的某 個檔案不能公開。或許我把我的信用卡號記錄在了一個文本檔案裡,而我又意外的把它 加入到了這個項目中。僅僅刪除這個檔案是不夠的,因為從別的提交記錄中還是可以訪 問到這個檔案。因此我們必須從所有的提交記錄中徹底刪除這個檔案。 $ git filter-branch --tree-filter 'rm top/secret/file' HEAD 參見 *git help filter-branch* ,那裡討論了這個例子並給出一個更快的方法。一般 地, *filter-branch* 允許你使用一個單一命令來大範圍地更改歷史。 此後,+.git/refs/original+目錄描述操作之前的狀態。檢查命令filter-branch的確做 了你想要做的,然後刪除此目錄,如果你想運行多次filter-branch命令。 最後,用你修訂過的版本替換你的項目克隆,如果你想之後和它們交互的話。 === 製造歷史 === [[makinghistory]] 想把一個項目遷移到Git嗎?如果這個項目是在用比較有名氣的系統,那可以使用一些其 他人已經寫好的腳本,把整個項目歷史記錄導出來放到Git裡。 否則,查一下 *git fast-import* ,這個命令會從一個特定格式的文本讀入,從頭來創 建Git歷史記錄。通常可以用這個命令很快寫一個腳本運行一次,一次遷移整個項目。 作為一個例子,粘貼以下所列到臨時檔案,比如/tmp/history: ---------------------------------- commit refs/heads/master committer Alice Thu, 01 Jan 1970 00:00:00 +0000 data < int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob Tue, 14 Mar 2000 01:59:26 -0800 data < int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- 之後從這個臨時檔案創建一個Git倉庫,鍵入: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history 你可以從這個項目checkout出最新的版本,使用: $ git checkout master . 命令*git fast-export* 轉換任意倉庫到 *git fast-import* 格式,你可以研究其輸 出來寫導出程序, 也以可讀格式傳送倉庫。的確,這些命令可以發送倉庫文本檔案 通過只接受文本的渠道。 === 哪兒錯了? === 你剛剛發現程序裡有一個功能出錯了,而你十分確定幾個月以前它運行的很正常。天啊! 這個臭蟲是從哪裡冒出來的?要是那時候能按照開發的內容進行過測試該多好啊。 現在說這個已經太晚了。然而,即使你過去經常提交變更,Git還是可以精確的找出問題所在: $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git從歷史記錄中檢出一個中間的狀態。在這個狀態上測試功能,如果還是有問題: $ git bisect bad 如果可以工作了,則把"bad"替換成"good"。Git會再次幫你找到一個以確定的好版本和 壞版本之間的狀態,通過這種方式縮小範圍。經過一系列的迭代,這種二分搜索會幫你 找到導致這個錯誤的那次提交。一旦完成了問題定位的調查,你可以返回到原始狀態, 鍵入: $ git bisect reset 不需要手工測試每一次改動,執行如下命令可以自動的完成上面的搜索: $ git bisect run my_script Git使用指定命令(通常是一個一次性的腳本)的返回值來決定一次改動是否是正確的: 命令退出時的代碼0代表改動是正確的,125代表要跳過對這次改動的檢查,1到127之間 的其他數值代表改動是錯誤的。返回負數將會中斷整個bisect的檢查。 你還能做更多的事情: 幫助文檔解釋了如何展示bisects, 檢查或重放bisect的日誌,並 可以通過排除對已知正確改動的檢查,得到更好的搜索速度。 === 誰讓事情變糟了? === 和其他許多版本控制系統一樣,Git也有一個"blame"命令: $ git blame bug.c 這個命令可以標註出一個指定的檔案裡每一行內容的最後修改者,和最後修改時間。但 不像其他版本控制系統,Git的這個操作是在綫下完成的,它只需要從本地磁碟讀取信息。 === 個人經驗 === 在一個中心版本控制系統裡,歷史的更改是一個困難的操作,並且只有管理員才有權這 麼做。沒有網絡,克隆,分支和合併都沒法做。像一些基本的操作如瀏覽歷史,或提交 變更也是如此。在一些系統裡,用戶使用網絡連接僅僅是為了查看他們自己的變更,或 打開檔案進行編輯。 中心繫統排斥離線工作,也需要更昂貴的網絡設施,特別是當開發人員增多的時候。最 重要的是,所有操作都一定程度變慢,一般在用戶避免使用那些能不用則不用的高級命 令時。在極端的情況下,即使是最基本的命令也會變慢。當用戶必須運行緩慢的命令的 時候,由於工作流被打斷,生產力降低。 我有這些的一手經驗。Git是我使用的第一個版本控制系統。我很快學會適應了它,用了 它提供的許多功能。我簡單地假設其他系統也是相似的:選擇一個版本控制系統應該和 選擇一個編輯器或瀏覽器沒啥兩樣。 在我之後被迫使用中心繫統的時候,我被震驚了。我那有些脆弱的網絡沒給Git帶來大麻 煩,但是當它需要像本地硬碟一樣穩定的時候,它使開發困難重重。另外,我發現我自 己有選擇地避免特定的命令,以避免踏雷,這極大地影響了我,使我不能按照我喜歡的 方式工作。 當我不得不運行一個慢的命令的時候,這種等待極大地破壞了我思緒連續性。在等待服 務器通訊完成的時候,我選擇做其他的事情以度過這段時光,比如查看郵件或寫其他的 文檔。當我返回我原先的工作場景的時候,這個命令早已結束,並且我還需要浪費時間 試圖記起我之前正在做什麼。人類不擅長場景間的切換。 還有一個有意思的大眾悲劇效應:預料到網絡擁擠,為了減少將來的等待時間,每個人 將比以往消費更多的頻寬在各種操作上。共同的努力加劇了擁擠,這等於是鼓勵個人下 次消費更多頻寬以避免更長時間的等待。 gitmagic-20160304/zh_tw/translate.txt0000644000175000017500000000262312666307504016674 0ustar sbadiasbadia== 附錄 B: 本指南的翻譯 == 我推薦如下步驟翻譯本指南,這樣我的腳本就可以快速生成HTML和PDF版本,並且所有翻 譯也可以共存於同一個倉庫。 克隆源碼,然後針對不同目標 http://www.iana.org/assignments/language-subtag-registry[語言的IETF tag]創建 一個目錄。參見 http://www.w3.org/International/articles/language-tags/Overview.en.php[ W3C在 國際化方面的文章 ]。例如,英語是“en”,日語是“ja”,正體中文是“zh-Hant”。 然後在新建目錄,翻譯這些來自“en”目錄的 +txt+ 檔案。 例如,將本指南譯為 http://en.wikipedia.org/wiki/Klingon_language[ 克林貢語 ], 你也許鍵入: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" 是克林貢語的IETF語言碼。 $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # 翻譯這個檔案 對每個檔案都一樣。 打開Makefile檔案,把語言碼加入`TRANSLATIONS`變數,現在你可以時不時查看你的工 作: $ make tlh $ firefox book-tlh/index.html 經常提交你的變更,然後然我知道他們什麼時候完成。GitHub.com提供一個便于fork “gitmatic”項目的界面,提交你的變更,然後告訴我去合併。 但請按照最適合你的方式做:例如,中文譯者就使用 Google Docs。只要你的工作使更多人看到我的工作,我就高興。 gitmagic-20160304/zh_tw/intro.txt0000644000175000017500000001352412666307504016034 0ustar sbadiasbadia== 入門 == 我將用類比方式來介紹版本控制的概念。更嚴謹的解釋參見 http://en.wikipedia.org/wiki/Revision_control[維基百科版本修訂控制條目]。 === 工作是玩 === 我從小就玩電腦遊戲。相反,我只是在長大後才開始使用版本控制系統。我想我並不特 殊,並且,對比兩者工作方式可使這些概念更易解釋,也易於理解。 編寫代碼,或編輯文檔和玩遊戲差不多。在你做出了很多進展之後,你最好保存一下。 去做這個,會點擊你所信任的編輯器保存按鈕就好了。 但這將覆蓋老版本。就像那些學校裡玩的老遊戲,只有一個存檔:你確實可以保存,但 你不能回到更老的狀態了。這真讓人掃興,因為那個狀態可能恰好保存了這個遊戲特別 有意思一關,說不定哪天你想再玩一下呢。或者更糟糕的,你當前的保存是個必敗局, 這樣你就不得不從頭開始玩了。 === 版本控制 === 在編輯的時候,如果想保留舊版本,你可以將檔案“另存為”一個不同的檔案,或在保 存之前將檔案拷貝到別處。你可能壓縮這些檔案以節省空間。這是一個初級的靠手工的 版本控制方式。遊戲軟件早就提高了這塊,很多都提供多個基于時間戳的自動存檔槽。 讓我們看看稍稍複雜的情況。比如你有很多放在一起的檔案,比如項目源碼,或網站文 件。現在如你想保留舊版本,你不得不把整個目錄存檔。手工保存多個版本很不方便, 而且很快會耗費巨大。 在一些電腦遊戲裡,一個存檔真的包含在一個充滿檔案的目錄裡。這些遊戲為玩家屏蔽 了這些細節,並提供一個方便易用的界面來管理該目錄的不同版本。 版本控制系統也沒有兩樣。兩者提供友好的界面,來管理目錄裡的東西。你可以頻繁保 存,也可以之後加載任一保存。不像大多計算機遊戲,版本控制系統通常精於節省存儲 空間。一般情況如果兩個版本間只有少數檔案的變更,每個檔案的變更也不大,那就只 存儲差異的部分,而不是把全部拷貝的都保存下來,以節省存儲空間。 === 分佈控制 === 現在設想一個很難的遊戲。太難打了,以至于世界各地很多骨灰級玩家決定組隊,分享 他們遊戲存檔以攻克它。Speedrun們就是實際中的例子:在同一個遊戲裡,玩家們分別 攻克不同的等級,協同工作以創造驚人戰績。 你如何搭建一個系統,使得他們易於得到彼此的存檔?並易於上載新的存檔? 在過去,每個項目都使用中心式版本控制。某個伺服器上放所有保存的遊戲記錄。其他 人就不用了。每個玩家在他們機器上最多保留幾個遊戲記錄。當一個玩家想更新進度時 候,他們需要把最新進度從主伺服器下載下來,玩一會兒,保存並上載到主伺服器以供 其他人使用。 假如一個玩家由於某種原因,想得到一個較舊版本的遊戲進度怎麼樣?或許當前保存的 遊戲是一個注定的敗局,因為某人在第三級忘記撿某個物品;他們希望能找到最近一個 可以完成的遊戲記錄。或者他們想比較兩個舊版本間的差異,來估算某個特定玩家幹了 多少活。 查看舊版本的理由有很多,但檢查的辦法都是一樣的。他們必須去問中心伺服器要那個 舊版本的記錄。需要的舊版本越多,和伺服器的交互就越多。 新一代的版本控制系統,Git就是其中之一,是分散式的,可以被認作廣義上的中心式系 統。從主伺服器下載時玩家會得到所有保存的記錄,而不僅是最新版。這看起來他們好 像把中心伺服器做了個鏡像。 最初的克隆操作可能比較費時,特別當有很長歷史的時,但從長遠看這是值得的。一個 顯而易見的好處是,當查看一個舊版本時,不再需要和中心伺服器通訊了。 === 一個誤區 === 一個很常見的錯誤觀念是,分散式系統不適合需要官方中心倉庫的項目。這與事實並不 相符。給誰照相也不會偷走他們的靈魂。類似地,克隆主倉庫並不降低它的重要性。 一般來說,一個中心版本控制系統能做的任何事,一個良好設計的分散式系統都能做得 更好。網絡資源總要比本地資源耗費更費。不過我們應該在稍後分析分散式方案的缺點, 這樣人們才不會按照習慣做出錯誤的比較。 一個小項目或許只需要分散式系統提供的一小部分功能,但是,在項目很小的時候,應 該用規劃不好的系統?就好比說,在計算較小數目的時候應該使用羅馬數字? 而且,你的項目的增長可能會超出你最初的預期。從一開始就使用Git好似帶著一把瑞士 軍刀,儘管你很多時候只是用它來開開瓶蓋。某天你迫切需要一把改錐,你就會慶幸你 所有的不單單是一個啟瓶器。 === 合併衝突 === 對於這個話題,電腦遊戲的類比顯得不夠用。那讓我們再來看看文檔編輯的情況吧。 假設Alice在文檔開頭插入一行,並且Bob在文檔末尾添加一行。他們都上傳了他們的改 動。大多數系統將自動給出一個合理的處理方式:接受且合併他們的改動,這樣Alice和 Bob兩人的改動都會生效。 現在假設Alice和Bob對檔案的同一行做了不同的改動。如果沒有人工參與的話,這個沖 突是無法解決的。第二個人在上載檔案時,會收到 _合併衝突_ 的通知,要麼用一個人 的改動覆蓋另一個的,要麼完全修訂這一行。 更複雜的情況也可能出現。版本控制系統自己處理相對簡單的情況,把困難的情況留給 人來處理。它們的行為通常是可配置的。 gitmagic-20160304/zh_tw/clone.txt0000644000175000017500000002267312666307504016006 0ustar sbadiasbadia== 克隆周邊 == 在較老一代的版本控制系統裡,checkout是獲取檔案的標準操作。你將獲得一組特定保 存狀態的檔案。 在Git和其他分散式版本控制系統裡,克隆是標準的操作。通過創建整個倉庫的克隆來 獲得檔案。或者說,你實際上把整個中心伺服器做了個鏡像。凡是主倉庫上能做的事, 你都能做。 === 計算機間同步 === 我可以忍受製作tar包或利用rsync來作備份和基本同步。但我有時在我筆記本上編輯, 其他時間在台式機上,而且這倆之間也許並不交互。 在一個機器上初始化一個Git倉庫並提交你的檔案。然後轉到另一台機器上: $ git clone other.computer:/path/to/files 以創建這些檔案和Git倉庫的第二個拷貝。從現在開始, $ git commit -a $ git pull other.computer:/path/to/files HEAD 將把另一台機器上特定狀態的檔案“拉”到你正工作的機器上。如果你最近對同一個文 件做了有衝突的修改,Git將通知你,而你也應該在解決衝突之後再次提交。 === 典型源碼控制 === 為你的檔案初始化Git倉庫: $ git init $ git add . $ git commit -m "Initial commit" 在中心伺服器,在某個目錄初始化一個“裸倉庫”: $ mkdir proj.git $ cd proj.git $ git init --bare $ touch proj.git/git-daemon-export-ok 如需要的話,啟動Git守護進程: $ git daemon --detach # 它也許已經在運行了 對一些Git伺服服務,按照其指導來初始化空Git倉庫。一般是在網頁上填一個表單。 把你的項目“推”到中心伺服器: $ git push central.server/path/to/proj.git HEAD 撿出源碼,可以鍵入: $ git clone central.server/path/to/proj.git 做了改動之後,開發保存變更到本地: $ git commit -a 更新到最近版本: $ git pull 所有衝突應被處理,然後提交: $ git commit -a 把本地改動撿入到中心倉庫: $ git push 如果主伺服器由於其他開發的活動,有了新的變更,這個撿入會失敗,該開發應該把最 新版本拿下來,解決合併衝突,然後重試。 為使用上面pull和push命令,開發必須有SSH訪問權限。不過,通過鍵入以下命令,任何 人都可以看到源碼: $ git clone git://central.server/path/to/proj.git 本地git協議和HTTP類似:並無安全驗證,因此任何人都能拿到項目。因此,預設情況 git協議禁止推操作。 === 封閉源碼 === 閉源項目不要執行touch命令,並確保你從未創建`git-daemon-export-ok`檔案。資源庫 不再可以通過git協議獲取;只有那些有SSH訪問權限的人才能看到。如果你所有的資源 庫都是封閉的,那也沒必要運行運行git守護了,因為所有溝通都走SSH。 === 裸倉庫 === 之所以叫裸倉庫是因為其沒有工作目錄;它只包含正常情況下隱藏在`.git`子目錄下 的檔案。換句話說,它維護項目歷史,而且從不保存任何給定版本的快照。 裸倉庫扮演的角色和中心版本控制系統中中心伺服器的角色類似:你項目的中心。開 發從其中克隆項目,撿入新近改動。典型地裸倉庫存在一個伺服器上,該伺服器除了 分散數據外並不做啥。開發活動發生在克隆上,因此中心倉庫沒有工作目錄也行。 很多Git命令在裸倉庫上失敗,除非指定倉庫路徑到環境變數`GIT_DIR`,或者指定 `--bare`選項。 === 推還是拽 === 為什麼我們介紹了push命令,而不是依賴熟悉的pull命令?首先,在裸倉庫上pull會 失敗:除非你必須“fetch”,一個之後我們要討論的命令。但即使我們在中心伺服器上 保持一個正常的倉庫,拽些東西進去仍然很繁瑣。我們不得不登陸伺服器先,給pull 命令我們要拽自機器的網絡地址。防火牆會阻礙,並且首先如果我們沒有到伺服器的 shell訪問怎麼辦呢? 然而,除了這個案例,我們反對推進倉庫,因為當目標有工作目錄時,困惑隨之而來。 簡短截說,學習Git的時候,只在目標是裸倉庫的時候push,否則用pull的方式。 === 項目分叉 === 項目走歪了嗎?或者認為你可以做得更好?那麼在伺服器上: $ git clone git://main.server/path/to/files 之後告訴每個相關的人你伺服器上項目的分支。 在之後的時間,你可以合併來自原先項目的改變,使用命令: $ git pull === 終極備份 === 會有很多散佈在各處,禁止篡改的冗餘存檔嗎? 如果你的項目有很多開發,那乾脆啥也 別做了。你的每份代碼克隆是一個有效備份。不僅當前狀態,還包括你項目整個歷史。 感謝哈希加密算法,如果任何人的克隆被損壞,只要他們與其他的交互,這個克隆就會 被修好。 如果你的項目並不是那麼流行,那就找儘可能多的伺服來放克隆吧。 真正的偏執狂應該總是把HEAD最近20位元組的SHA1哈希值寫到安全的地方。應該保證安全, 而不是把它藏起來。比如,把它發佈到報紙上就不錯,因為對攻擊者而言,更改每份報 紙是很難的。 === 輕快多任務 === 比如你想並行開發多個功能。那麼提交你的項目並運行: $ git clone . /some/new/directory Git使用硬連結和檔案共享來儘可能安全地創建克隆,因此它一眨眼就完成了,因此你現 在可以並行操作兩個沒有相互依賴的功能。例如,你可以編輯一個克隆,同時編譯另一 個。感謝 http://en.wikipedia.org/wiki/Hard_link[hardlinking], 本地克隆比簡單 備份省時省地。 現在你可以同時工作在兩個彼此獨立的特性上。比如,你可以在編譯一個克隆的時候編 輯另一個克隆。任何時候,你都可以從其它克隆提交並拖拽變更。 $ git pull /the/other/clone HEAD === 游擊版本控制 === 你正做一個使用其他版本控制系統的項目, 而你非常思念Git? 那麼在你的工作目錄初 始化一個Git倉庫: $ git init $ git add . $ git commit -m "Initial commit" 然後克隆它: $ git clone . /some/new/directory 並在這個目錄工作,按你所想在使用Git。過一會,一旦你想和其他每個人同步,在這種 情況下,轉到原來的目錄,用其他的版本控制工具同步,並鍵入: $ git add . $ git commit -m "Sync with everyone else" 現在轉到新目錄運行: $ git commit -a -m "Description of my changes" $ git pull 把你的變更提交給他人的過程依賴于其他版本控制系統。這個新目錄包含你的改動的文 件。需要運行其他版本控制系統的命令來上載這些變更到中心倉庫。 Subversion, 或許是最好的中心式版本控制系統,為無數項目所用。 *git svn* 命令為 Subversion倉庫自動化了上面的操作,並且也可以用作 http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[ 導出Git項目到Subversion倉庫] 的替代。 === Mercurial === Mercurial是一個類似的的版本控制系統,几乎可以和Git一起無縫工作。使用 `hg-git`插件,一個Mercurial用戶可以無損地往Git倉庫推送,從Git倉庫拖拽。 使用Git獲得`hg-git`插件: $ git clone git://github.com/schacon/hg-git.git 或使用Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ 不好意思,我沒注意Git有類似的插件。因此, 我主張使用Git而不是Mercurial作為主資 源庫,即使你偏愛Mercurial。使用Mercurial項目,通常一個自願者維護一個平行的 Git項目以適應Git用戶,然而感謝`hg-git`插件,一個Git項目自動地適應Mercurial用 戶。 儘管該插件可以把一個Mercurial倉庫轉成一個Git倉庫,通過推到一個空的倉庫, 這個差事交給`hg-fast-export.sh`腳本還是更容易些。來自: $ git clone git://repo.or.cz/fast-export.git 要轉化,只需在一個空目錄運行: $ git init $ hg-fast-export.sh -r /hg/repo 注意該腳本應加入你的`$PATH`。 === Bazaar === 我們簡略提一下Bazaar,它畢竟是緊跟Git和Mercurial之後最流行的自由分散式版本控 制系統。 Bazaar有後來者的優勢,它相對年輕些;它的設計者可以從前人的錯誤中學習,並且躲 過去翻歷史上犯過的錯誤。另外,它的開發人員對可移植性以及和與其它版本控制系統 的互操作性也考慮周全。 一個`bzr-git`插件讓Bazaar用戶在一定程度下可以工作在Git倉庫。`tailor`程序轉 換Bazaar倉庫到Git倉庫,並且可以遞增的方式做,要知道`bzr-fast-export`只是 在一次性轉換性情況下工作良好。 === 我偏愛Git的原因 === 我起先選擇Git是因為我聽說它能管理不可想象地不可管理的Linux內核源碼。我從來沒 覺得有離開的必要。Git已經服侍的很好了,並且我也沒有被其瑕疵所困擾。因為我主要 使用Linux,其他平台上的問題與我無關。 還有,我偏愛C程序和bash腳本,以及諸如Python的可執行可腳本:較少依賴,並且我也 沉迷于快速的執行時間。 我考慮過Git才能如何提高,甚至自己寫類似的工具,但只作為研究練練手。即使完成這 個項目,我也無論如何會繼續使用Git,因為使用一個古裡古怪的系統所獲甚微。 自然地,你的需求和期望可能不同,並且你可能使用另一個系統會好些。儘管如此,使 用Git你都錯不太遠。 gitmagic-20160304/fop-vi.xsl0000644000175000017500000000115412666307504014731 0ustar sbadiasbadia DejaVu Serif DejaVu Sans ExtraLight DejaVu Serif Bold gitmagic-20160304/fop-vi.xconf0000644000175000017500000000035212666307504015237 0ustar sbadiasbadia /usr/share/fonts/truetype/ttf-dejavu/ gitmagic-20160304/ChangeLog0000644000175000017500000013621212666307504014557 0ustar sbadiasbadiaWed, 13 Jan 2016 10:34:31 +0800 - fix typo Wed, 13 Jan 2016 09:48:41 +0800 - fix typo Tue, 12 Jan 2016 19:49:04 +0800 - fix some typo and pick up new added part. Mon, 30 Nov 2015 14:15:33 +0100 - (fr) misc typofixes. Sun, 4 Jan 2015 16:33:46 +0100 - proofread finished Wed, 31 Dec 2014 10:17:01 +0100 - correct typos2 Fri, 26 Dec 2014 08:58:50 +0100 - correct typo Thu, 25 Dec 2014 10:10:01 +0100 - Typo Tue, 9 Dec 2014 10:13:03 +0800 - fix typo regarding ORIG_HEAD in zh_cn/grandmaster.txt Mon, 24 Nov 2014 23:51:00 -0800 - Switch to Open Sans. Mon, 24 Nov 2014 23:37:27 -0800 - Removed broken link. Sat, 25 Oct 2014 15:55:36 +0700 - vi: Use fop to create book-vi.pdf Sat, 30 Aug 2014 14:57:52 +0700 - vi: Fix typo and update preface: Thu, 14 Aug 2014 19:44:37 -0700 - Fixes to Korean translation. Thu, 14 Aug 2014 08:42:49 -0400 - grandmastery.txt completed 2 more to go Thu, 14 Aug 2014 06:42:07 -0400 - grandmaster.txt is alsmost finished...two more documents to go Wed, 13 Aug 2014 22:34:18 -0400 - tonight's (8-13-2014) work Wed, 13 Aug 2014 18:16:38 -0400 - some changes were made after transitioning my worksstation to mac Wed, 23 Jul 2014 07:42:59 +0900 - 65% complete -- I am on multiplayer.txt Sun, 20 Jul 2014 12:59:47 +0900 - 50% completed Sat, 19 Jul 2014 03:34:00 +0900 - ko -- 40% completed Wed, 16 Jul 2014 08:46:41 +0900 - ko folder created and files copied from en folder Wed, 18 Dec 2013 19:58:07 +0100 - Typo-Korrekturen6 Tue, 17 Dec 2013 20:40:10 +0100 - Typo-Korrekturen5 Tue, 17 Dec 2013 20:30:00 +0100 - Typo-Korrekturen4 Tue, 17 Dec 2013 20:12:22 +0100 - Typo-Korrekturen3 Tue, 17 Dec 2013 19:56:09 +0100 - Typo-Korrekturen2 Tue, 17 Dec 2013 19:41:34 +0100 - Typo-Korrekturen Sun, 27 Oct 2013 09:47:37 +0100 - Faute de participe passé Thu, 17 Oct 2013 22:15:55 -0300 - Update drawbacks.txt Fri, 4 Oct 2013 20:39:53 -0700 - Spanish translation of secrets.txt by Oscar Uribe. Sat, 6 Jul 2013 23:19:48 -0400 - Mention on it version in de, en, fr versions Sat, 6 Jul 2013 23:09:58 -0400 - Added it/drawbacks.txt Sat, 6 Jul 2013 19:41:29 -0400 - Added it/secrets.txt Sat, 6 Jul 2013 17:17:16 +0200 - master is now clean Sat, 6 Jul 2013 16:54:34 +0200 - Polish translation ready Wed, 3 Jul 2013 22:17:52 +0200 - first quick and dirty translation from german translation using omegat Tue, 2 Jul 2013 12:41:24 +0200 - created fork on github.com and added pl directory for polish translation Sun, 23 Jun 2013 23:06:02 -0400 - Added italian version of branch.txt Sun, 23 Jun 2013 23:00:52 -0400 - Small corrections to preface.txt Tue, 11 Jun 2013 00:26:07 -0400 - Added clone.txt Sat, 6 Jul 2013 15:29:09 +0200 - szl del from master Sat, 6 Jul 2013 14:59:08 +0200 - Alle Dateien in pl geprüft2 Sat, 6 Jul 2013 14:33:36 +0200 - Alle Dateien in pl geprüft Sat, 6 Jul 2013 12:12:50 +0200 - some changes in pl Sat, 6 Jul 2013 04:58:40 +0200 - changes in pl/intro.txt and pl/preface.txt Fri, 5 Jul 2013 12:38:16 -0400 - Added it/grandmaster.txt Fri, 5 Jul 2013 16:55:47 +0200 - Silesian language added Fri, 5 Jul 2013 16:46:01 +0200 - Kleinigkeiten korigiert Fri, 5 Jul 2013 13:06:34 +0200 - first version compleet Fri, 5 Jul 2013 11:16:25 +0200 - files secrets.txt translate.txt Fri, 5 Jul 2013 02:39:54 +0200 - nultiplayer.txt rechecked Fri, 5 Jul 2013 02:19:24 +0200 - intro.txt rechecked Fri, 5 Jul 2013 02:04:57 +0200 - history.txt rechecked Fri, 5 Jul 2013 01:49:45 +0200 - grandmaster.txt rechecked Fri, 5 Jul 2013 01:38:18 +0200 - drawback.txt rechecked Fri, 5 Jul 2013 01:17:38 +0200 - clone.txt rechecked Fri, 5 Jul 2013 00:28:43 +0200 - zwischendurch Thu, 4 Jul 2013 23:36:45 +0200 - probleme Thu, 4 Jul 2013 23:36:06 +0200 - probleme Thu, 4 Jul 2013 22:47:26 +0200 - prepared for make test Thu, 4 Jul 2013 22:38:09 +0200 - branch test created Thu, 4 Jul 2013 17:56:05 +0200 - file history.txt translated partially Thu, 4 Jul 2013 16:58:50 +0200 - file grandmasters.txt ready Thu, 4 Jul 2013 15:57:32 +0200 - file drawbacks.txt ready Thu, 4 Jul 2013 15:20:15 +0200 - file clone.txt ready Thu, 4 Jul 2013 12:06:45 +0200 - file brunch.txt ready Thu, 4 Jul 2013 01:24:35 +0200 - file basic.txt ready Wed, 3 Jul 2013 22:17:52 +0200 - first quick and dirty translation from german translation using omegat Tue, 2 Jul 2013 23:56:14 -0400 - Added it/multiplayer.txt Tue, 2 Jul 2013 12:41:24 +0200 - created fork on github.com and added pl directory for polish translation Sun, 30 Jun 2013 16:26:07 -0400 - Fixed some minor typos in it/translate.txt Sun, 30 Jun 2013 16:22:20 -0400 - Fixed some minor typos in it/intro.txt Sun, 30 Jun 2013 16:20:54 -0400 - Fixed some minor typos in it/basic.txt Sun, 30 Jun 2013 16:17:37 -0400 - Fixed some minor typos in it/clone.txt Sun, 30 Jun 2013 16:10:27 -0400 - Fixed some typos in it version of branch.txt Sun, 30 Jun 2013 15:39:06 -0400 - Added it version of history.txt Wed, 26 Jun 2013 23:58:52 -0700 - Brazilian Portuguese translation. Sun, 23 Jun 2013 23:06:02 -0400 - Added italian version of branch.txt Sun, 23 Jun 2013 23:00:52 -0400 - Small corrections to preface.txt Tue, 11 Jun 2013 00:26:07 -0400 - Added clone.txt Mon, 3 Jun 2013 23:08:49 -0400 - Added basic.txt intro.txt preface.txt translate.txt to it version Mon, 3 Jun 2013 23:02:30 -0400 - First release of it version Mon, 27 May 2013 08:23:47 +0700 - l10n: vi: Update Vietnamese translation: Wed, 26 Dec 2012 15:53:23 +0200 - translation completed Tue, 25 Dec 2012 15:25:24 +0800 - delete Tue, 18 Dec 2012 22:51:04 -0300 - Remove repeated consecutively words in clone.txt, intro.txt and drawbacks.txt files of the Spanish version. Sun, 16 Dec 2012 16:09:31 +0200 - finished translations of some files Fri, 14 Dec 2012 08:00:35 -0300 - Realizo varias correcciones ortográficas (y algunas gramaticales y de contexto) en todos los archivos (menos en drawbacks.txt y spanish.txt), cambio "foto" por "instantánea" (me parece que es una mejor traducción para "snapshot") y actualizo un párrafo de clone.txt a partir de la version en ingles. También traduzco el ejemplo de la sección "Haciendo Historia" de history.txt y los ejemplos que aparecen a lo largo de multiplayer.txt. Sun, 9 Dec 2012 19:26:54 -0800 - Updated credits. Wed, 14 Nov 2012 23:48:18 +0200 - Додано посилання на переклад у файли preface.txt. Перекладено повністю. Залишилось лише вичитати переклад. Wed, 14 Nov 2012 23:39:10 +0200 - повністю перекладено uk/translate.txt Wed, 14 Nov 2012 23:32:54 +0200 - повністю перекладено uk/intro.txt Wed, 14 Nov 2012 22:55:52 +0200 - повністю перекладено uk/secrets.txt Wed, 7 Nov 2012 18:38:33 +0100 - small corrections German spelling and grammar Tue, 6 Nov 2012 00:49:54 +0200 - повністю перекладено uk/multiplayer.txt Tue, 30 Oct 2012 01:47:55 +0200 - повністю перекладено uk/grandmaster.txt Sat, 13 Oct 2012 00:59:38 +0300 - перекладено до 50 рядка uk/grandmaster.txt Fri, 12 Oct 2012 23:00:06 +0300 - повністю перекладено uk/drawbacks.txt Sun, 7 Oct 2012 19:39:15 +0300 - повністю перекладено uk/branch.txt Tue, 25 Sep 2012 22:04:45 +0300 - частково перекладено (до 40 рядка) файл uk/branch.txt Tue, 25 Sep 2012 21:20:05 +0300 - перекладено повністю uk/history.txt Sat, 22 Sep 2012 02:05:44 +0300 - made some translations in uk/history.txt Sat, 22 Sep 2012 01:37:56 +0300 - fully translated uk/preface.txt into ukrainian Sat, 22 Sep 2012 01:09:43 +0300 - added uk to TRANSLATIONS into Makefile Sat, 22 Sep 2012 01:05:21 +0300 - made some edits of uk/clone.txt Sat, 22 Sep 2012 00:43:24 +0300 - fully translated uk/clone.txt into ukrainian Fri, 21 Sep 2012 22:52:13 +0300 - uk/basic.txt was fully translated on ukrainian Fri, 21 Sep 2012 09:26:46 +0300 - made more translations of basic.txt on ukrainian Thu, 20 Sep 2012 19:23:28 +0300 - made some translation of basic.txt on ukrainian Thu, 21 Jun 2012 03:48:48 -0500 - elimina repetición "de de" Wed, 16 May 2012 23:15:09 +0530 - New git site URL Sun, 15 Apr 2012 14:11:41 +0700 - Update translation in credits. Fri, 13 Apr 2012 19:23:21 +0200 - FR: some more mispellings. Wed, 11 Apr 2012 23:45:55 -0700 - Updated credits. Wed, 11 Apr 2012 18:32:35 +0300 - Minor spelling errors. Mon, 2 Apr 2012 11:35:26 +0300 - Update ru/branch.txt Sun, 25 Mar 2012 08:40:02 +0700 - Update from english. Mon, 12 Mar 2012 23:57:21 -0700 - Added two mirrors. Wed, 7 Mar 2012 13:16:26 +0200 - mistype Wed, 7 Mar 2012 13:11:28 +0200 - a bit af russian terms corrections Tue, 6 Mar 2012 21:16:48 -0500 - deleting extra line in English Fri, 17 Feb 2012 18:53:02 -0800 - A couple of corrections. Fri, 3 Feb 2012 13:28:52 +0700 - Fix Ubuntu package URLs. Thu, 2 Feb 2012 13:13:05 -0600 - Eliminted extra space. Thu, 2 Feb 2012 11:39:57 -0600 - Corrected to get backup files right. Wed, 1 Feb 2012 14:55:49 -0600 - Add -e for compliance on mac with sed. Sun, 29 Jan 2012 21:19:35 +0100 - fix the lack of cohesion with 'git checkout -b chef' Sun, 15 Jan 2012 23:25:11 -0800 - Corrected links in Russian preface. Fri, 13 Jan 2012 14:09:57 +0700 - Update vi/preface.txt after merge from origin project. Sun, 18 Dec 2011 20:54:19 +0800 - translate zh_tw/preface.txt Sun, 27 Nov 2011 14:08:29 -0800 - Added Assembla mirror. Mon, 21 Nov 2011 13:25:44 +0700 - Fix stupid error: source text file must in ANSI as UTF-8, not just UTF-8. Mon, 21 Nov 2011 08:32:00 +0700 - Minor edit and fix typos Mon, 14 Nov 2011 14:38:39 +0100 - Added assembla to git free hosts Fri, 11 Nov 2011 14:41:53 +0700 - Update translation for Vietnamese from 9d0f8fae commit. Thu, 10 Nov 2011 11:28:32 -0800 - Fixed user manual link. Thu, 28 Jul 2011 01:28:20 -0700 - Minor edits. Mon, 11 Jul 2011 10:29:36 +1000 - Added more details to rebasing - cloning, --abort Sun, 10 Jul 2011 17:30:50 +0200 - new: finished translation of de/preface.txt Sun, 10 Jul 2011 15:17:39 +0200 - new: finished translating de/drawbacks.txt Fri, 8 Jul 2011 00:56:28 -0700 - Edited previous commit to suit my style. Thu, 7 Jul 2011 19:06:29 +0200 - edit: corrected typos, solved problem with umlauts Thu, 7 Jul 2011 15:25:20 +1000 - Added a more detailed explanation of rebase squashing Tue, 5 Jul 2011 23:49:18 +0200 - new: translated de/secrets.txt Tue, 5 Jul 2011 16:46:16 +0800 - Chinese translation lastest update Mon, 4 Jul 2011 18:53:05 +0200 - edit: typos in de/grandmaster.txt Mon, 4 Jul 2011 18:29:52 +0200 - edit: translated updated de/pot/*.po files and generate de/*.txt files after merge upstream/master Mon, 4 Jul 2011 18:24:52 +0200 - new: added the po4all gereated de/grandmaster.txt Sun, 3 Jul 2011 19:55:17 +0200 - edit: finished grandmaster.txt Sun, 3 Jul 2011 19:54:16 +0200 - edit: translated one forgotten section Fri, 24 Jun 2011 21:42:03 +0200 - fix masculine adjective by feminine adjective. Thu, 16 Jun 2011 15:15:34 -0700 - Updated "Classic source control". Sat, 11 Jun 2011 14:31:19 +0700 - Minor fix typos. Sun, 1 May 2011 07:59:58 +0700 - Retranslated and update Vietnamese. Tue, 26 Apr 2011 14:27:29 +0800 - Introduction enhancement Fri, 15 Apr 2011 09:45:20 +0800 - Remove miss left Tue, 5 Apr 2011 09:11:46 +0300 - ru/drawbacks.txt: misprint fixed Mon, 28 Mar 2011 20:58:39 +0300 - ru/multiplayer.txt: misprint fixed Mon, 28 Mar 2011 11:37:04 +0800 - zh_cn zh_tw PDF support Wed, 23 Mar 2011 11:09:14 +0200 - ru/* final corrections Thu, 17 Mar 2011 19:57:50 +0200 - ru/* updated Thu, 17 Mar 2011 18:38:03 +0200 - ru/*: магия -> волшебство Thu, 17 Mar 2011 18:36:23 +0200 - Makefile: added -p to mkdir to avoid error message when dir exists. Tue, 8 Mar 2011 16:35:55 +0800 - Fix link Tue, 8 Mar 2011 16:29:54 +0800 - Fix typo error Tue, 8 Mar 2011 00:19:58 -0800 - Added Vietnamese support to the new Makefile. Tue, 8 Mar 2011 09:43:15 +0800 - Correct format error Tue, 8 Mar 2011 09:43:15 +0800 - Correct format error Tue, 8 Mar 2011 08:45:39 +0800 - cddf translation and .gitignore update Tue, 8 Mar 2011 09:43:15 +0800 - Correct format error Tue, 8 Mar 2011 09:43:15 +0800 - Correct format error Tue, 8 Mar 2011 08:45:39 +0800 - cddfa translation and .gitignore update Mon, 7 Mar 2011 10:51:18 -0800 - Fixed links to Chinese translations. Mon, 7 Mar 2011 01:54:49 -0800 - Removed Makefile targets which only I use. Mon, 7 Mar 2011 14:44:40 +0800 - Correct misunderstanding Sun, 6 Mar 2011 17:47:30 +0800 - Update online URLs Sun, 6 Mar 2011 16:46:57 +0800 - Convert to zh_tw Sun, 6 Mar 2011 16:35:50 +0800 - Rename to zh_cn Sun, 6 Mar 2011 13:30:06 +0800 - Format adjustment. Sun, 6 Mar 2011 13:08:06 +0800 - Fix print error Sun, 6 Mar 2011 12:58:58 +0800 - Simplified Chinese translation. Tue, 1 Mar 2011 18:39:59 +0200 - Makefile: separate targets for each language, book-??.* Mon, 28 Feb 2011 13:48:04 +0100 - typo corrections Mon, 28 Feb 2011 01:05:44 +0100 - update french translators name Sun, 27 Feb 2011 18:30:54 +0100 - review of fr/translate.txt Sun, 27 Feb 2011 18:30:31 +0100 - review of fr/drawbacks.txt Sun, 27 Feb 2011 18:20:11 +0100 - review of fr/secrets.txt Sun, 27 Feb 2011 16:54:13 +0100 - review of fr/grandmaster.txt Sun, 27 Feb 2011 16:11:45 +0100 - review of fr/history.txt Sun, 27 Feb 2011 15:53:51 +0100 - review of fr/multiplayer.txt Sun, 27 Feb 2011 15:38:00 +0100 - review of fr/branch.txt Sun, 27 Feb 2011 12:26:13 +0100 - review of fr/clone.txt Sun, 27 Feb 2011 12:08:40 +0100 - review of fr/basic.txt Sun, 27 Feb 2011 11:44:25 +0100 - review of fr/intro.txt Sun, 27 Feb 2011 11:43:58 +0100 - review of fr/preface.txt Sat, 26 Feb 2011 16:14:48 +0100 - reflow a paragraph Sat, 26 Feb 2011 15:38:31 +0100 - update fr/preface.tx from en/preface.txt Tue, 22 Feb 2011 19:16:19 +0100 - remplacement de clé par empreinte (un oubli) Sat, 26 Feb 2011 02:56:43 -0800 - Removed BOM from Vietnamese files. Sat, 26 Feb 2011 08:26:01 +0700 - Fix homepage link. Missing users Sat, 26 Feb 2011 08:20:42 +0700 - Minor edit preface: add link to personal site Fri, 25 Feb 2011 13:09:48 +0700 - Mino edit README Thu, 24 Feb 2011 15:37:00 +0700 - Complete edit ecrets. And also all of files! Wed, 23 Feb 2011 14:13:25 +0700 - Edit secrets Wed, 23 Feb 2011 13:58:28 +0700 - Complete edit drawbacks Sun, 20 Feb 2011 10:33:10 +0300 - ./ru style fixes Sun, 20 Feb 2011 09:53:06 +0300 - ./ru typo fixes Mon, 21 Feb 2011 14:50:27 +0700 - Edit secrets and drawbacks Mon, 21 Feb 2011 14:17:56 +0700 - Complete edit grandmaster Sun, 20 Feb 2011 19:42:51 +0100 - ajout des espaces insécables et remplacement de 'hash' par 'empreinte'. Sun, 20 Feb 2011 14:59:36 +0700 - Minor editing secrets. Sat, 19 Feb 2011 23:23:51 +0100 - fr: correction d'entete emacs de fr/drawbacks.txt Sat, 19 Feb 2011 23:29:21 +0100 - fr: un meilleur titre dans fr/secrets.txt Sun, 13 Feb 2011 18:51:02 +0100 - fr: mise a jour depuis la version anglaise Sat, 19 Feb 2011 23:23:02 +0100 - fr: fin de traduction de fr/translate.txt Sat, 19 Feb 2011 23:04:57 +0100 - fr: fin de traduction de fr/drawbacks.txt Sat, 19 Feb 2011 17:43:05 +0100 - fr: entete emacs - passage en 79 colonnes Sat, 19 Feb 2011 02:51:55 +0100 - fr: traduction en cours de fr/drawbacks.txt Sat, 19 Feb 2011 01:31:14 +0100 - fr: fin de traduction de fr/secrets.txt Fri, 18 Feb 2011 17:58:48 +0100 - fr: traduction en cours de fr/secrets.txt Fri, 18 Feb 2011 13:33:52 +0100 - fr: fin de traduction de fr/grandmaster.txt Fri, 18 Feb 2011 01:05:04 +0100 - fr: traduction en cours fr/grandmaster.txt Thu, 17 Feb 2011 18:32:57 +0100 - fr: correction de grammaire dans fr/multiplayer.txt Thu, 17 Feb 2011 18:32:33 +0100 - fr: traduction en cours de fr/grandmaster.txt Thu, 17 Feb 2011 01:46:12 +0100 - fr: fin de traduction de fr/multiplayer.txt Wed, 16 Feb 2011 18:03:53 +0100 - fr: meilleur entete pour emacs Wed, 16 Feb 2011 18:02:51 +0100 - fr: traduction en cours de fr/multiplayer.txt Sat, 19 Feb 2011 23:59:02 +0100 - fr: fin de traduction de fr/history.txt Tue, 15 Feb 2011 18:50:50 +0100 - fr: fin de traduction de branch.txt Tue, 15 Feb 2011 18:50:14 +0100 - fr: correction de jeu/jeux dans fr/intro.txt Tue, 15 Feb 2011 18:49:35 +0100 - fr: correction d'un titre dans fr/clone.txt Tue, 15 Feb 2011 18:49:07 +0100 - fr: correction typo dans fr/preface.txt Tue, 15 Feb 2011 01:16:15 +0100 - fr: traduction en cours de fr/branch.txt Mon, 14 Feb 2011 01:19:23 +0100 - fr: traduduction en cours de fr/branch.txt Sun, 13 Feb 2011 18:50:00 +0100 - fr: orthographe Sat, 12 Feb 2011 14:54:02 +0100 - fr: corrections de styles, de grammaire et d'orthographe Sat, 19 Feb 2011 08:33:56 +0700 - Complete edit history, editing secrets. Thu, 17 Feb 2011 14:50:16 +0700 - Complete edit multiplayer, translate, edit history 90%, edit secret. Wed, 16 Feb 2011 13:26:48 +0700 - Complete edit branch, add .gitignore. Add special thanks in README. Edit history Wed, 16 Feb 2011 09:05:02 +0700 - Complete edit basic, clone, intro, preface Tue, 15 Feb 2011 14:27:02 +0700 - Complete update data to f8eb610 Sat, 12 Feb 2011 09:22:37 +0700 - Update data Thu, 10 Feb 2011 08:00:07 +0700 - continue edit intro.txt Tue, 8 Feb 2011 16:57:35 -0800 - Note: I meant "Ariset Llerena", not "Ariset Tapia". Fri, 4 Feb 2011 17:38:42 +0100 - update french translation for intro, clone and preface. Mon, 31 Jan 2011 17:05:04 +0100 - corrections on french translation for preface Mon, 31 Jan 2011 16:29:25 +0100 - corrections on french translation for basic Mon, 31 Jan 2011 16:08:35 +0100 - corrections on french translation for intro Fri, 28 Jan 2011 05:10:47 +0200 - en/* updated to translation state Fri, 28 Jan 2011 05:10:00 +0200 - ru/preface.txt: new translations info added Fri, 28 Jan 2011 05:09:10 +0200 - ru/clone.txt updated Thu, 27 Jan 2011 00:32:17 -0800 - Link to French translation. Wed, 26 Jan 2011 22:02:44 -0800 - Credited Ariset Tapia. Wed, 26 Jan 2011 21:35:24 -0800 - Link to Russian translation. Mon, 24 Jan 2011 19:18:48 +0100 - corrected typos Fri, 14 Jan 2011 14:35:27 +0700 - Continue edit secrets.txt Thu, 13 Jan 2011 17:43:30 +0100 - corrections on french translation for clone. Tue, 11 Jan 2011 15:03:44 +0100 - fin de traduction de clone.txt Fri, 7 Jan 2011 23:55:50 -0300 - Translation to spanish of file multiplayer.txt Tue, 4 Jan 2011 14:52:26 +0700 - Continue edit secrets.txt Tue, 4 Jan 2011 14:17:18 +0700 - Continue edit history.txt Tue, 14 Dec 2010 20:54:52 +0100 - finished multiplayer.txt Sat, 1 Jan 2011 08:08:15 +0700 - Continue edit history.txt Fri, 31 Dec 2010 08:37:22 +0700 - Continue edit history.txt Fri, 31 Dec 2010 08:37:22 +0700 - Continue edit history.txt Thu, 30 Dec 2010 14:24:41 +0700 - start edit history.txt Thu, 30 Dec 2010 14:02:28 +0700 - contiune edit branch.txt Thu, 30 Dec 2010 09:09:39 +0700 - Mino edit basic.txt, contiune edit branch.txt Wed, 29 Dec 2010 09:07:16 +0700 - continue edit branch.txt Tue, 28 Dec 2010 08:27:25 +0700 - continue edit branch.txt Mon, 27 Dec 2010 14:34:17 +0700 - Edit branch.txt Mon, 27 Dec 2010 14:27:05 +0700 - Edit branch.txt Mon, 27 Dec 2010 13:58:46 +0700 - Edit grandmaster.txt Mon, 27 Dec 2010 13:55:03 +0700 - Edit grandmaster.txt Mon, 27 Dec 2010 13:43:47 +0700 - Edit multiplayer.txt Sat, 25 Dec 2010 14:58:22 +0700 - Edit multiplayer.txt and secrets.txt Sat, 25 Dec 2010 08:18:07 +0700 - Edit multiplayer.txt Sat, 25 Dec 2010 08:16:27 +0700 - Edit multiplayer.txt Sat, 25 Dec 2010 08:06:47 +0700 - Edit multiplayer.txt Fri, 24 Dec 2010 14:39:18 +0700 - Edit grandmaster.txt Fri, 24 Dec 2010 14:29:44 +0700 - Edit grandmaster.txt Fri, 24 Dec 2010 08:51:27 +0700 - minor edit preface.txt Fri, 24 Dec 2010 08:48:59 +0700 - Start edit branch.txt Fri, 24 Dec 2010 08:38:01 +0700 - Edit multiplayer.txt Thu, 23 Dec 2010 09:22:41 +0700 - Edit grandmaster.txt Thu, 23 Dec 2010 09:17:49 +0700 - Edit grandmaster.txt Thu, 23 Dec 2010 08:37:48 +0700 - Edit grandmaster.txt Wed, 22 Dec 2010 14:38:27 +0700 - Edit grandmaster.txt Wed, 22 Dec 2010 09:39:05 +0700 - Edit grandmaster.txt Wed, 22 Dec 2010 09:19:13 +0700 - Edit intro.txt Tue, 21 Dec 2010 08:32:07 +0700 - Continue edit intro.txt and multiplayer.txt Mon, 20 Dec 2010 15:22:30 +0300 - Localize $TITLE Mon, 20 Dec 2010 14:58:28 +0700 - Continue edit intro.txt and drawbacks.txt Mon, 20 Dec 2010 09:15:29 +0700 - Continue edit intro.txt Sun, 19 Dec 2010 15:19:06 +0700 - Continue edit intro.txt drawback.txt grandmaster.txt Sun, 19 Dec 2010 09:35:53 +0700 - Complete edit clone.txt Fri, 17 Dec 2010 20:29:07 +0200 - many minor edits in ru/*.txt Fri, 17 Dec 2010 18:13:08 +0200 - ru/preface.txt: translation info brokes asciidoc templates; removed (temporarily?) Fri, 17 Dec 2010 14:55:01 +0200 - Russian translation updated Wed, 15 Dec 2010 14:06:44 +0700 - Minor edit clone.txt Wed, 15 Dec 2010 09:03:05 +0700 - fix double white space in preface.txt Wed, 15 Dec 2010 08:55:16 +0700 - minor edit clone.txt Wed, 15 Dec 2010 08:27:20 +0700 - minor edit translate.txt Wed, 15 Dec 2010 08:23:57 +0700 - minor edit preface.txt Tue, 14 Dec 2010 14:34:04 +0700 - continue edit intro.txt Tue, 14 Dec 2010 13:55:20 +0700 - continue edit drawback.txt Tue, 14 Dec 2010 09:00:55 +0700 - Continue edit clone.txt and drawback.txt Tue, 14 Dec 2010 07:45:06 +0700 - minor edit basic.txt Mon, 13 Dec 2010 15:06:28 +0700 - complete basic.txt Mon, 13 Dec 2010 14:56:04 +0700 - continue translate drawbacks.txt Mon, 13 Dec 2010 14:54:42 +0700 - continue translate drawbacks.txt Mon, 13 Dec 2010 14:14:13 +0700 - init translate drawbacks.txt Sun, 12 Dec 2010 20:39:26 -0800 - Edited Tyler Breisacher's contributions. Mon, 13 Dec 2010 08:25:53 +0700 - continue edit clone.txt Sun, 12 Dec 2010 08:34:58 +0700 - continue edit clone.txt Sat, 11 Dec 2010 14:33:53 +0700 - continue edit clone.txt Sat, 11 Dec 2010 09:17:23 +0700 - continue edit clone.txt Thu, 9 Dec 2010 14:56:19 +0700 - Started translate clone.txt Thu, 9 Dec 2010 09:07:27 +0700 - nearly complete basic.txt Wed, 8 Dec 2010 17:11:52 +0200 - ru/preface.txt: translation info updated Thu, 16 Dec 2010 16:24:05 +0200 - ru/basic.txt: punctustion corrected Tue, 7 Dec 2010 14:45:59 +0700 - Continue edit basic.txt Tue, 7 Dec 2010 09:08:17 +0700 - Continue edit basic.txt Tue, 7 Dec 2010 08:52:19 +0700 - Continue edit basic.txt Tue, 7 Dec 2010 08:05:30 +0700 - Minor edit preface.txt, previous is translate.txt Tue, 7 Dec 2010 07:58:18 +0700 - Minor edit preface.txt Mon, 6 Dec 2010 14:02:02 +0700 - Complete stranslate preface.txt Sun, 5 Dec 2010 09:23:31 +0700 - Start translate preface.txt Sun, 5 Dec 2010 07:14:05 +0700 - 50% preface.txt Sat, 4 Dec 2010 14:44:43 +0700 - Complete translate translate.txt, half of basic.txt Sat, 4 Dec 2010 09:00:50 +0700 - Innit text file Sat, 4 Dec 2010 08:04:31 +0700 - Innit project Sun, 28 Nov 2010 23:35:50 -0800 - Don't use "git checkout -b" if we're just going to checkout a different branch right after Sun, 28 Nov 2010 23:31:27 -0800 - clarify that git checkout -b actually does two separate things Sun, 28 Nov 2010 23:30:23 -0800 - put the comment next to the line it describes, instead of after it Sun, 28 Nov 2010 23:24:44 -0800 - small grammatical error Mon, 15 Nov 2010 11:15:42 +0100 - work in progress on french transaltion for clone.txt Sun, 14 Nov 2010 19:04:48 +0100 - finished history.txt Fri, 5 Nov 2010 22:25:18 +0100 - forgot to add the txt file Fri, 5 Nov 2010 22:22:02 +0100 - finished branch.txt Fri, 5 Nov 2010 18:46:08 +0100 - started branch.txt Fri, 5 Nov 2010 18:16:45 +0100 - po4a changes made by running 'make update' in de/ Fri, 5 Nov 2010 18:11:38 +0100 - po4a-update, added missing master file format Thu, 4 Nov 2010 23:13:51 -0700 - Updated credits. Thu, 4 Nov 2010 18:28:14 +0100 - work in progress on clone Wed, 3 Nov 2010 00:52:32 +0100 - finished clone.txt Tue, 2 Nov 2010 19:22:25 +0100 - started clone.txt Mon, 1 Nov 2010 22:42:48 +0100 - transferred basic.txt into po4a, corrected typos Mon, 1 Nov 2010 22:40:51 +0100 - finished intro.txt, translate.txt Mon, 1 Nov 2010 22:39:52 +0100 - po4a files for german translation Mon, 1 Nov 2010 22:37:55 +0100 - use po4a for translating Mon, 1 Nov 2010 22:35:36 +0100 - ignore the generated book* files Tue, 19 Oct 2010 23:38:09 -0700 - es/ markup fixes. Thu, 14 Oct 2010 09:37:55 +0200 - work in progress on french translation of intro Wed, 13 Oct 2010 09:53:56 +0200 - work in progress on french translation for intro Tue, 12 Oct 2010 09:27:36 +0200 - work in progress on french translation for intro Mon, 11 Oct 2010 10:02:36 +0200 - first pass on french translation of preface Fri, 8 Oct 2010 19:38:18 +0200 - work in progress on french translation of preface Fri, 8 Oct 2010 14:52:31 +0200 - unreviewed translation of basic.txt Sun, 3 Oct 2010 01:40:43 +0400 - Базовые операции: подправил предложение. Sun, 3 Oct 2010 01:34:19 +0400 - Базовые операции: стилистические правки Wed, 29 Sep 2010 01:12:31 +0400 - ru/intro.txt, Глупые предрассудки: style fixes. Wed, 29 Sep 2010 01:09:23 +0400 - ru/intro.txt, Распределенное управление: style fixes. Wed, 29 Sep 2010 01:03:49 +0400 - ru/intro.txt, Управление версиями: style fixes. Wed, 29 Sep 2010 00:57:50 +0400 - ru/intro.txt, Work is Play: style fixes. Sun, 19 Sep 2010 00:50:29 -0700 - Minor touch-ups. Sat, 4 Sep 2010 20:38:34 -0700 - de/basic.txt heading fixes. Fri, 3 Sep 2010 15:41:18 +0200 - basic.txt is ready Fri, 3 Sep 2010 15:21:34 +0200 - up to exercises Fri, 3 Sep 2010 15:15:57 +0200 - typo Fri, 3 Sep 2010 15:15:42 +0200 - typo Tue, 24 Aug 2010 09:24:58 +0300 - reformulated one sentence in ru/secrets.txt Sun, 22 Aug 2010 22:03:39 +0300 - ru/translate.txt edited Sun, 22 Aug 2010 21:57:50 +0300 - ru/drawbacks.txt edited Sun, 22 Aug 2010 20:28:26 +0300 - ru/secrets.txt edited Wed, 18 Aug 2010 14:39:19 +0300 - ru/grandmaster.txt editing finished Sun, 8 Aug 2010 21:51:49 +0300 - ru/*: abbrevs replaced with full words (см. -> смотрите, и т.д. -> и так далее and so on) Sun, 8 Aug 2010 21:34:12 +0300 - ru/grandmaster.txt editing: stopped half-way for global fix Mon, 2 Aug 2010 15:27:36 +0300 - ru/grandmaster.txt editing: four chapters left Sun, 1 Aug 2010 21:45:51 +0300 - ru/multiplayer.txt editing finished Sun, 1 Aug 2010 16:59:03 +0300 - ru/multyplayer.txt editing: four chapters left Sat, 31 Jul 2010 20:48:01 +0300 - ru/history.txt edited Wed, 28 Jul 2010 14:34:06 +0300 - ru/branch.txt editing finished Tue, 27 Jul 2010 15:30:39 +0300 - ru/clone.txt editing finished Tue, 27 Jul 2010 11:35:37 +0300 - ru/clone.txt editing: two chapters left Tue, 27 Jul 2010 10:57:18 +0300 - ru/clone.txt editing: five chapters left Sun, 25 Jul 2010 15:46:52 +0300 - ru/basic.txt: misprint fixed Fri, 16 Jul 2010 19:39:58 +0300 - editing ru/basic.txt finished Fri, 16 Jul 2010 17:53:21 +0800 - ru/basic.txt editing: two last chapters left Fri, 16 Jul 2010 12:53:21 +0300 - ru/basic.txt editing: two last chapters left Fri, 16 Jul 2010 12:55:32 +0400 - Assignment of "preface" and "appendix" templates to chapters in Russian translation is fixed. Wed, 14 Jul 2010 13:35:30 +0400 - ru/multiplayer.txt: reformatting Wed, 14 Jul 2010 13:27:41 +0400 - Many YOs changed to YEs. Wed, 14 Jul 2010 03:00:05 +0400 - ru/branch.txt: "Work How You Want" translation completed Mon, 12 Jul 2010 10:10:07 +0300 - first half of ru/basic.txt edited Sat, 10 Jul 2010 20:30:16 +0300 - ru/intro edited Sat, 10 Jul 2010 10:50:06 +0400 - history: заменил слово на более подходящее Sat, 10 Jul 2010 10:46:27 +0400 - history: Заменил слво на, ИМХО, более подходящее словосочетание Sat, 10 Jul 2010 00:09:18 +0300 - ru/*: fixed контроль версий -> управление версиями Fri, 9 Jul 2010 23:45:02 +0300 - editing ru/intro.txt: stopped half-way for global fix Fri, 9 Jul 2010 23:10:34 +0300 - minor edits in ru/multiplayer.txt and ru/grandmaster.txt Fri, 9 Jul 2010 21:25:19 +0400 - grandmaster: исправление опечатки Fri, 9 Jul 2010 21:15:32 +0400 - grandmaster: уточнил перевод фразы. Имхо, так более точно и по смыслу, ближе к оригиналу и более стройно звучит Fri, 9 Jul 2010 21:10:28 +0400 - grandaster: Изменил перевод фразы на, имхо, более точный Fri, 9 Jul 2010 19:37:15 +0300 - punctuation fix in ru/history.txt Fri, 9 Jul 2010 20:24:06 +0400 - diff для history.txt Fri, 9 Jul 2010 19:33:50 +0400 - base.txt: Заменил "осваиваете" на "изучаете" по скольку в русском тексте это звучит стройнее. Да и в исходном тексте "learning" Fri, 9 Jul 2010 18:03:06 +0300 - fixed errors in ru/history.txt Fri, 9 Jul 2010 17:24:40 +0300 - minor edit in ru/branch.txt Fri, 9 Jul 2010 17:21:23 +0300 - reverted (manualy) some punctuations Fri, 9 Jul 2010 17:58:22 +0400 - punctuation and misprints Fri, 9 Jul 2010 16:39:00 +0300 - Translated forgotten sample filenames in ru/basic.txt Fri, 9 Jul 2010 15:16:10 +0300 - Revert "В пример были переведены директории. Восстановил английский вариант." Fri, 9 Jul 2010 15:06:59 +0300 - Applying forgotten changes from master branch Fri, 9 Jul 2010 15:30:50 +0400 - secrets.txt: Исправлено несколько опечаток Fri, 9 Jul 2010 15:18:18 +0400 - multiplayer.txt: Добавлена пропущенная буква Fri, 9 Jul 2010 15:07:58 +0400 - Удалил лишний перевод строки разбивающий пердложение. Fri, 9 Jul 2010 14:59:29 +0400 - Убраны лишние буквы. Fri, 9 Jul 2010 14:12:12 +0400 - Исправлена опечатка Fri, 9 Jul 2010 14:07:18 +0400 - Убрал лишнюю букву Fri, 9 Jul 2010 13:23:20 +0400 - В пример были переведены директории. Восстановил английский вариант. Fri, 9 Jul 2010 00:05:49 +0300 - ru/clone.txt minor edits Thu, 8 Jul 2010 23:01:35 +0300 - ru/preface.txt minor edits Thu, 8 Jul 2010 20:12:43 +0300 - ru/branch.txt: started translating of last chapter Thu, 8 Jul 2010 21:09:45 +0400 - * ru/clone.txt: translated "Mercurial", "Bazaar", "Why I use Git" sections. Thu, 8 Jul 2010 11:35:08 +0300 - ru/branch.txt: "Uninterrupted Workflow" chapter translated Tue, 6 Jul 2010 21:47:16 +0300 - ru/branch.txt: "Merging" chapter translated Sun, 4 Jul 2010 00:29:50 +0300 - minor edits in ru/history.txt Sat, 3 Jul 2010 08:57:50 +0300 - minor edits in ru/clone.txt Sat, 3 Jul 2010 07:30:31 +0300 - ru/drawbacks.txt editing finished Fri, 2 Jul 2010 19:33:10 +0300 - ru/drawbacks.txt editing: four chapters left Fri, 2 Jul 2010 15:00:51 +0300 - translated forgotten capter in ru/secrets.txt Fri, 2 Jul 2010 14:50:25 +0300 - ru/secrets.txt edited and updated to upstream english version Fri, 2 Jul 2010 10:19:23 +0300 - ru/secrets.txt editing and updating: five chapters left Fri, 2 Jul 2010 08:19:24 +0300 - ru/preface.txt: added small chapter "From Translation Editor" Fri, 2 Jul 2010 07:45:33 +0300 - ru/translate.txt final edit; updating not needed Thu, 1 Jul 2010 19:58:12 +0300 - fixed one forgoten translation mistake inru/grandmaster.txt Thu, 1 Jul 2010 19:54:23 +0300 - ru/grandmaste.txt edited and updated to upstream english version Thu, 1 Jul 2010 10:49:27 +0400 - ru/clone.txt: "Push versus pull" title translation Thu, 1 Jul 2010 10:48:42 +0400 - ru/clone.txt: translation for "Bare repositories", "Push versus pull" Thu, 1 Jul 2010 08:12:17 +0300 - updated forgotten original file: en/multiplayer.txt Wed, 30 Jun 2010 22:05:18 +0300 - ru/multyplayer.txt updated to upstream english version Wed, 30 Jun 2010 21:13:07 +0300 - fixed: репозит[оа]рий -> хранилище Wed, 30 Jun 2010 21:11:05 +0300 - editing ru/multiplayer.txt finished Wed, 30 Jun 2010 13:36:31 +0400 - - ru/clone.txt: wording changes (partially). - en/clone.txt: updated to upstream version. Wed, 30 Jun 2010 12:06:10 +0300 - ru/history.txt updated to upstream english version Wed, 30 Jun 2010 13:11:39 +0400 - ru/basic.txt: wording changes Wed, 30 Jun 2010 11:48:09 +0300 - updated ru/clone.txt, without five new chapters: "bare repos", "push vs pull" and the three last Wed, 30 Jun 2010 11:13:54 +0300 - ru/preface.txt updated to upstream english version Wed, 30 Jun 2010 12:10:55 +0400 - ru/intro.txt: styling within a couple of paragraphs Wed, 30 Jun 2010 12:06:02 +0400 - ru/intro.txt: small changes for style. Wed, 30 Jun 2010 10:55:34 +0300 - updated ru/branch.txt, without chapters "merging", "unint. workflow" and "work how you want": theese chapters should be retranslated from scratch Wed, 30 Jun 2010 11:37:18 +0400 - ru/preface.txt: small changes for style. Wed, 30 Jun 2010 10:29:19 +0300 - updated forgotten original files: en/intro.txt en/basic.txt Wed, 30 Jun 2010 09:58:56 +0300 - ru/basic.txt updated to upstream english version Wed, 30 Jun 2010 09:35:11 +0300 - ru/intro.txt updated to upstream english version Wed, 30 Jun 2010 08:43:21 +0300 - en/clone.txt: removed non-translated (and obsolete) chapter "push vs pull" Tue, 29 Jun 2010 20:19:27 +0300 - done with ru/history.txt editing Tue, 29 Jun 2010 19:49:15 +0300 - ru/history.txt: all but several last paragraphs is done Tue, 29 Jun 2010 16:18:34 +0300 - ru/history.txt: first three chapters edited Tue, 29 Jun 2010 13:42:02 +0400 - You/Your/Yours capitalization fix. Tue, 29 Jun 2010 12:41:03 +0400 - * Style fixes. * A few translation fixes. Tue, 29 Jun 2010 10:29:27 +0300 - forgotten first two paragraphs in ru/branch.txt edited Tue, 29 Jun 2010 09:50:13 +0300 - editing ru/branch.txt finished Tue, 29 Jun 2010 09:14:07 +0300 - ru/branch.txt editing: two chapters left Tue, 29 Jun 2010 08:40:26 +0300 - ru/branch.txt editing: three chapters left Tue, 29 Jun 2010 07:33:29 +0300 - ru/branch.txt first chapter edited Tue, 29 Jun 2010 06:23:17 +0300 - ru/clone.txt final edit Tue, 29 Jun 2010 06:23:17 +0300 - ru/basic.txt final edit Tue, 29 Jun 2010 06:23:17 +0300 - ru/intro.txt final edit Tue, 29 Jun 2010 06:23:17 +0300 - ru/preface.txt final edit Sat, 10 Apr 2010 09:57:17 -0700 - Added link to physcial book. Mon, 5 Apr 2010 18:02:54 -0700 - Reworded Git Grandmastery intro. Mon, 29 Mar 2010 23:57:10 +0200 - up top line 166 Mon, 29 Mar 2010 23:54:22 +0200 - typos Sun, 28 Mar 2010 18:36:38 -0700 - Minor tweaks. Fri, 26 Mar 2010 01:21:10 -0700 - Expanded index explanation. Fri, 26 Mar 2010 00:38:49 -0700 - Minor edits. Fri, 12 Mar 2010 04:42:07 +0100 - Three minor typo fixes. Fri, 12 Mar 2010 04:38:29 +0100 - Add "reword" and "fixup" commands used by git rebase -i Mon, 8 Mar 2010 02:37:07 -0800 - Minor edits. Mon, 8 Mar 2010 01:26:53 -0800 - Made more examples concrete. Mon, 8 Mar 2010 00:57:00 -0800 - Converted most subsections to sections. Mon, 8 Mar 2010 00:44:45 -0800 - Added subsection on merging. Thu, 4 Mar 2010 11:47:31 -0800 - All examples in basic.txt now concrete. Thu, 4 Mar 2010 10:51:30 -0800 - Made some examples look more realistic. Thu, 4 Mar 2010 10:06:53 -0800 - A few words on Mercurial and Bazaar. Wed, 3 Mar 2010 12:33:13 -0800 - Makefile fix for asciidoc-8.4.5. Sun, 28 Feb 2010 22:19:27 -0800 - Minor edits. Thu, 25 Feb 2010 23:50:13 -0200 - Listed 2 new files Thu, 25 Feb 2010 23:37:55 -0200 - Drawbacks fully translated Thu, 25 Feb 2010 22:35:08 -0200 - Grandmaster fully translated Thu, 25 Feb 2010 03:20:33 -0200 - History fully translated Mon, 15 Feb 2010 15:45:28 +0100 - up to :122 unverzügliches veröffentlichen Sun, 7 Feb 2010 17:43:17 -0800 - Added Thomas Miedema to credits. Tue, 2 Feb 2010 16:36:35 +0100 - line 118, bleeding edge Tue, 2 Feb 2010 16:20:14 +0100 - typo Sat, 30 Jan 2010 12:08:28 +0300 - * fix error (к небольшим изменениям) Sat, 30 Jan 2010 00:50:42 +0100 - line 62 Fri, 29 Jan 2010 20:42:35 +0100 - up to line 52 Fri, 29 Jan 2010 16:05:58 +0900 - Remove shortcoming rebasing an unrelated branch. Thu, 28 Jan 2010 23:39:23 +0300 - * fix text style (чуть позже мы поговорим об этом) Thu, 28 Jan 2010 18:20:39 +0100 - Add, Delete, Rename Thu, 28 Jan 2010 18:08:09 +0100 - initial commit started with basic.txt Sun, 17 Jan 2010 19:33:15 +0300 - Apply patch by Dmitry Medvinsky, thanks Sat, 16 Jan 2010 15:06:25 +0300 - Apply patch by Sosnovskiy Alexander, thank you Sat, 9 Jan 2010 09:39:57 +0300 - Adding untranslated file in ru-folder translate.txt Fri, 8 Jan 2010 02:28:21 +0300 - missing header = Fri, 8 Jan 2010 02:22:42 +0300 - last enter in secrets Fri, 8 Jan 2010 02:05:50 +0300 - style fin in multiplayer (6.5) Fri, 8 Jan 2010 02:01:36 +0300 - Text style fixing Thu, 7 Jan 2010 18:49:25 +0300 - * in all book 'коммит' not 'фиксация' Thu, 7 Jan 2010 18:47:00 +0300 - * fix enter Thu, 7 Jan 2010 18:45:28 +0300 - * fix enter in branch Thu, 7 Jan 2010 18:41:03 +0300 - * fix some formating and errors Thu, 7 Jan 2010 18:37:48 +0300 - * fix format in branch.txt Thu, 7 Jan 2010 18:33:45 +0300 - * fix in clone (3.6) Thu, 7 Jan 2010 18:25:14 +0300 - * fix in 2.6 not normal text Thu, 7 Jan 2010 18:20:20 +0300 - * fix : and paragraph in 2.6 Mon, 4 Jan 2010 03:02:06 +0300 - * Adding draft translation secrets.txt Mon, 4 Jan 2010 02:38:20 +0300 - * Исправление Git-хостинг Mon, 4 Jan 2010 02:03:12 +0300 - Adding draft translation drawbacks.txt Sat, 2 Jan 2010 00:22:01 +0300 - Adding translate of grandmaster.txt file Tue, 17 Nov 2009 23:09:24 -0800 - Makefile fix for AsciiDoc 8.5.1. Sun, 15 Nov 2009 15:22:51 -0800 - Fixes thanks to Sébastien Hinderer. Fri, 13 Nov 2009 00:16:13 +0300 - Adding draft translate multiplayer.txt Tue, 20 Oct 2009 01:55:11 +0400 - Adding tranlation history.txt and correct small errors in others files Wed, 14 Oct 2009 13:33:39 -0700 - Updated credits. Sun, 4 Oct 2009 22:30:26 +0200 - Remove two no longer relevant problem from the drawbacks section Fri, 9 Oct 2009 16:44:12 -0700 - Updated credits. Thu, 17 Sep 2009 22:18:00 +0200 - Fixed a typo in en/grandmaster.txt Sat, 26 Sep 2009 02:45:46 +0200 - Typo fixes. Mon, 21 Sep 2009 00:20:03 +0400 - Rough translation preface.txt Wed, 16 Sep 2009 14:37:07 +0400 - Translate branch.txt to russian language Tue, 8 Sep 2009 20:39:02 -0700 - New Debian package maintainer. Tue, 1 Sep 2009 02:46:05 -0700 - Minor tweak suggested by Andy Somerville. Wed, 5 Aug 2009 14:47:07 -0700 - Rewrote "Uninterrupted Workflow". Wed, 15 Jul 2009 12:55:35 -0700 - Reorganized "Thanks!". Wed, 15 Jul 2009 12:36:01 -0700 - Linked to Portuguese translation. Fri, 26 Jun 2009 15:38:39 -0700 - Interface quirks. Fri, 26 Jun 2009 14:12:33 -0700 - Forgot I had introduced push in an earlier chapter. Fri, 26 Jun 2009 02:10:47 -0700 - Split Grandmastery chapter. Thu, 25 Jun 2009 02:12:12 -0700 - Changed hook example. Thu, 25 Jun 2009 01:58:54 -0700 - Moved ChangeLog generation tip. Thu, 25 Jun 2009 01:45:46 -0700 - Wrote about hooks. Wed, 24 Jun 2009 11:08:05 -0300 - Now really added branch.txt Wed, 24 Jun 2009 11:07:30 -0300 - Now really added clone.txt Wed, 24 Jun 2009 01:38:25 -0300 - Branch fully translated Tue, 23 Jun 2009 23:36:24 -0300 - Clone fully translated Tue, 23 Jun 2009 14:11:46 -0700 - Simplify an example. Tue, 23 Jun 2009 17:05:23 +0400 - Переведена глава Cloning Around Sun, 21 Jun 2009 22:29:39 +0400 - Удалены синтаксические ошибки разметки, возникшие из-за перевода Sun, 21 Jun 2009 22:04:48 +0400 - Revert "Adding missing file" Sun, 21 Jun 2009 21:11:34 +0400 - Adding missing file Sat, 20 Jun 2009 14:04:48 -0700 - I had forgotten to add translate.txt. Sun, 21 Jun 2009 00:28:29 +0400 - Translated basic.txt with translated.by service. Url: http://translated.by/you/git-magic-chapter-2-basic-tricks/ Tue, 16 Jun 2009 15:10:58 +0400 - Test more commit Tue, 16 Jun 2009 14:55:08 +0400 - Test push from another user Tue, 16 Jun 2009 14:49:32 +0400 - Translated intro.txt (draft version) Tue, 16 Jun 2009 14:05:18 +0400 - first commit Tue, 9 Jun 2009 00:50:13 -0700 - Minor changes. Tue, 9 Jun 2009 00:02:15 -0700 - Moved translation hints to appendix. Mon, 8 Jun 2009 23:47:50 -0700 - In newer AsciiDoc versions, should not escape "--". Fri, 5 Jun 2009 02:14:35 -0700 - Wrote about remote repositories. Thu, 7 May 2009 02:34:18 -0700 - Minor edits to The Object Database section. Thu, 7 May 2009 02:14:47 -0700 - Linked to Spanish version, translation howto. Wed, 6 May 2009 23:54:30 -0300 - Added file which tracks original english versions. Wed, 6 May 2009 23:53:57 -0300 - Basic fully translated Wed, 6 May 2009 23:52:07 -0300 - Fixed untranslated paragraph Tue, 5 May 2009 01:21:24 -0700 - Minor Makefile tweak. Tue, 5 May 2009 01:10:16 -0700 - Use English versions for untranslated chapters. Tue, 5 May 2009 01:04:21 -0700 - Remove duplicate files. Allow partial translations. Tue, 5 May 2009 00:17:14 -0700 - Forgot to commit the untranslated files in "es". Mon, 4 May 2009 23:52:27 -0700 - Can build partially translated Spanish version. Mon, 4 May 2009 22:06:01 -0700 - Moved text files to "en" subdirectory. Mon, 4 May 2009 21:18:54 -0700 - Moved translated files to new subdirectory. Mon, 4 May 2009 20:22:48 -0700 - Changed an example to suggest a Shakespeare quote. Sun, 3 May 2009 21:20:46 -0700 - How the object database works, with an example. Sun, 3 May 2009 01:09:07 -0300 - Intro fully translated Sun, 3 May 2009 00:06:07 -0300 - Preface fully translated Mon, 20 Apr 2009 18:49:07 -0700 - Wrote about patches. Fri, 6 Mar 2009 01:55:05 -0800 - Minor tweaks. Fri, 6 Mar 2009 01:27:26 -0800 - Briefly explain the index. Fri, 6 Mar 2009 00:33:00 -0800 - Added "Exercise" section. Thu, 5 Mar 2009 23:32:04 -0800 - Elaborated "Building on Git" section. Mon, 5 Jan 2009 00:04:30 -0800 - Typo fixes thanks to Frode Aannevik. Mon, 22 Dec 2008 17:04:30 -0800 - Minor fix. Sat, 20 Dec 2008 20:59:48 -0500 - In section "Work is Play", replaced "a exceptionally" with "an exceptionally". Tue, 16 Dec 2008 04:28:14 -0500 - Added missing word "to" in section "Building on Git". Sun, 14 Dec 2008 18:12:02 -0800 - Moved another line out of Basic Tricks chapter. Sun, 14 Dec 2008 17:56:50 -0800 - Moved reflog-related commands out of Basic Tricks chapter. Sun, 14 Dec 2008 16:58:27 -0800 - Mentioned clean, fixed whitespace issues. Thu, 11 Dec 2008 01:58:02 -0800 - Added "Git Over Anything" section. Fri, 5 Dec 2008 00:10:05 -0800 - Added link to Chinese translation. Sun, 30 Nov 2008 14:01:08 -0800 - Described bisect and blame. Sat, 15 Nov 2008 01:42:18 -0800 - Added instructions for getting the source. Tue, 11 Nov 2008 18:43:24 -0800 - Added license (GPLv3). Sun, 9 Nov 2008 13:50:18 -0800 - Moved Personal Experience section to chapter on history. Fri, 7 Nov 2008 14:37:34 -0800 - Minor edits. Thu, 6 Nov 2008 17:59:51 -0800 - Spun off some sections into a new chapter on history operations. Thu, 6 Nov 2008 16:43:59 -0800 - Minor edits. Wed, 5 Nov 2008 15:56:40 -0800 - Added section on stash command. Wed, 5 Nov 2008 13:50:31 -0800 - Described git add -p, mentioned git branch -d, -m. Wed, 5 Nov 2008 04:52:01 -0800 - Minor edits. Thu, 7 Aug 2008 20:08:23 -0700 - PDF and single-page editions. Tue, 5 Aug 2008 21:22:40 -0700 - Minor corrections. Tue, 5 Aug 2008 21:06:24 -0700 - Mentioned reflog. Tue, 5 Aug 2008 20:51:50 -0700 - Removed confusing push paragraph. Tue, 5 Aug 2008 20:49:45 -0700 - Another minor rewording. Tue, 5 Aug 2008 20:41:35 -0700 - Minor edit. Tue, 5 Aug 2008 20:36:17 -0700 - Elaborate on checkout safety. Tue, 5 Aug 2008 20:28:36 -0700 - Safeguards, and how to override them. Wed, 6 Aug 2008 11:47:41 +1000 - trivial typos noted while reading Tue, 10 Jun 2008 02:39:24 -0700 - Mention index. Fri, 30 May 2008 23:34:30 -0700 - Minor edits. Fri, 30 May 2008 23:28:12 -0700 - Added Derek to credits. Fri, 30 May 2008 17:36:51 -0400 - Fixed typo in section "Reorganizing a Medley". Thu, 22 May 2008 17:26:56 -0700 - Added simpler solution to Commit What Changed. Thu, 22 May 2008 16:46:09 -0700 - Linked to msysgit, as suggested by Tarmigan. Tue, 20 May 2008 07:55:06 -0700 - Added fast-import tip. Tue, 20 May 2008 01:43:26 -0700 - Described cherry-picks. Tue, 20 May 2008 00:46:56 -0700 - More intro edits. Tue, 20 May 2008 00:40:09 -0700 - Intro edits. Thu, 15 May 2008 00:12:35 -0700 - Add github.com host Wed, 14 May 2008 16:07:46 -0700 - Minor typo Wed, 14 May 2008 15:48:44 -0700 - Removed links from preface. Thu, 8 May 2008 21:22:28 -0700 - Fixed == typo. Thu, 1 May 2008 21:28:49 -0700 - Split Git Shortcomings into new chapter, added to it Sun, 27 Apr 2008 09:21:48 -0700 - Minor preface fixes. Mon, 14 Apr 2008 02:25:46 -0700 - Minor edits. Mon, 14 Apr 2008 01:34:27 -0700 - Fixed description of revert. Sun, 13 Apr 2008 23:49:01 -0700 - Applied suggestions from Douglas Livingstone. Sun, 13 Apr 2008 10:07:48 -0300 - Add a tip referencing git svn Sun, 13 Apr 2008 10:07:47 -0300 - Update the "Automatic compression" entry to reflect the new git gc behaviour Sun, 13 Apr 2008 10:07:46 -0300 - Remove clone -l -s references Sun, 13 Apr 2008 10:07:45 -0300 - Normalize capitalization of commit messages Sun, 13 Apr 2008 10:07:44 -0300 - Remove an obsolete tip Sun, 13 Apr 2008 21:24:29 -0700 - Credit contributors Sun, 13 Apr 2008 02:29:08 -0700 - Tiny edits. Sat, 12 Apr 2008 09:10:53 -0700 - Modernized command invocations a bit. Mon, 7 Apr 2008 01:33:35 -0700 - Minor fixes Sat, 22 Mar 2008 23:35:26 -0700 - Added more git hosting sites. Tue, 18 Mar 2008 09:37:46 -0700 - Aliases, printing current branch Tue, 18 Mar 2008 01:51:56 -0700 - Update Google Analytics Javascript Tue, 4 Dec 2007 17:54:59 -0800 - Added HEAD-hunting section Tue, 2 Oct 2007 23:46:59 -0700 - Minor fixes Mon, 24 Sep 2007 03:01:50 -0700 - Moved Google Analytics code to makeover script Mon, 24 Sep 2007 02:03:24 -0700 - Minor Makefile bugfix Sun, 23 Sep 2007 14:42:20 -0700 - Added Google Analytics code Sun, 23 Sep 2007 12:46:26 -0700 - Converted to AsciiDoc Tue, 18 Sep 2007 03:24:51 -0700 - Reworded new sections in Git Grandmastery Tue, 18 Sep 2007 03:07:47 -0700 - Added git-rebase sections Tue, 11 Sep 2007 01:36:42 -0700 - Added push target to Makefile Fri, 7 Sep 2007 20:51:43 -0700 - Reordered analogies in "Work How You Want" Fri, 7 Sep 2007 19:17:22 -0700 - Added repo.or.cz link Fri, 7 Sep 2007 18:25:12 -0700 - Wrote "Don't Lose Your HEAD" Thu, 6 Sep 2007 14:24:58 -0700 - More salesmanship in branch chapter Tue, 4 Sep 2007 03:02:05 -0700 - Added section on rename and copy detection Tue, 4 Sep 2007 02:38:57 -0700 - Minor bugfix in lightspeed multitasking section Tue, 4 Sep 2007 02:33:05 -0700 - Style changes Tue, 4 Sep 2007 02:08:20 -0700 - Added top-level itemized lists to wiki format Tue, 4 Sep 2007 00:15:57 -0700 - Rephrased a few sentences Mon, 3 Sep 2007 23:57:36 -0700 - Many minor edits Mon, 3 Sep 2007 23:54:38 -0700 - Added two chapters Sun, 2 Sep 2007 19:57:06 -0700 - Added references Sun, 2 Sep 2007 11:25:02 -0700 - Correction on effects of git-checkout Sun, 2 Sep 2007 10:35:42 -0700 - Wrote about branches Sun, 2 Sep 2007 01:19:45 -0700 - Fixed URL link conversion bug Sat, 1 Sep 2007 12:19:16 -0700 - Moved some secions from basic.txt to clone.txt Fri, 31 Aug 2007 02:13:18 -0700 - More detail in second chapter Thu, 30 Aug 2007 17:28:55 -0700 - Second chapter Thu, 30 Aug 2007 07:32:08 -0700 - minor edits Thu, 30 Aug 2007 07:19:56 -0700 - scripts for converting to HTML Thu, 30 Aug 2007 06:29:35 -0700 - Introduction gitmagic-20160304/custom-nochunks.xsl0000644000175000017500000000047112666307504016672 0ustar sbadiasbadia gitmagic-20160304/es/0000755000175000017500000000000012666307504013407 5ustar sbadiasbadiagitmagic-20160304/es/drawbacks.txt0000644000175000017500000001722712666307504016122 0ustar sbadiasbadia== Apéndice A: Defectos de Git == Hay algunos problema con Git que barrí bajo la alfombra. Algunos pueden ser manejados con facilidad utilizando scripts y hooks, otros requieren reorganizar or redefinir el proyecto, y por las molestias que quedan, uno simplemente va a tener que esperar. ¡O mejor aún, unirse y ayudar! === Debilidades De SHA1 === A medida que pasa el tiempo, los criptógrafos descubren más y más debilidades de SHA1. Al día de hoy, encontrar colisiones en los hashes es feasible para organizaciones con buenos fondos. En unos años, quizás incluso una PC típica tendrá suficiente poder de cómputo para corromper un repositorio de Git de manera silenciosa. Esperemos que Git migre a una función de hash mejor antes de que nuevas investigaciones destruyan el SHA1. === Microsoft Windows === Git en Microsoft Windows puede ser engorroso: - http://cygwin.com/[Cygwin], un ambiente similar a Linux para Windows, contiene http://cygwin.com/packages/git/[una versión de Git para Windows]. - http://code.google.com/p/msysgit/[Git en MSys] es una alternativa que requiere un soporte mínimo para la ejecución, aunque algunos de los comandos necesitan cierto trabajo. === Archivos No Relacionados === Si tu proyecto es muy grande y contiene muchos archivos no relacionados que están siendo cambiados de manera constante, Git puede estar en desventaja ante otros sistemas, porque no se monitorean archivos simples. Git maneja proyectos enteros, lo cual suele ser beneficioso. Una solución es partir tu proyecto en pedazos, cada uno consistiendo de archivos relacionados. Usa *git submodule* si quieres mantener todo en un único repositorio. === ¿Quién Edita Qué? === Algunos sistemas de control de versiones te fuerzan a marcar un archivo de manera explícita antes de editarlo. Si bien esto es especialmente molesto cuando esto involucra comunicarse con un servidor central, también tiene dos beneficios: 1. Los diffs son rápidos porque solo se precisa examinar los archivos marcados. 2. Uno puede descubrir quién más está trabajando en el archivo preguntándole al servidor central quien lo marcó para edición. Con scripts apropiados, se puede alcanzar lo mismo con Git. Esto requiere cooperación del programador, quien debería ejecutar ciertos scripts cuando edita un archivo. === Historia Por Archivo === Como Git guarda cambios por proyecto, reconstruir la historia de un archivo dado requiere más trabajo que en sistemas de control de versiones que administran archivos individuales. El problema suele ser leve, y vale tenerlo dado que otras operaciones son increíblemente eficientes. Por ejemplo, `git checkout` es más rápido que `cp -a`, y los deltas de un proyecto completo comprimen mejor que colecciones de deltas por archivo. === Clonado inicial === Cuando hay una historia larga, crear un clon es más costoso que hacer checkout de código en otros sistemas de control de versiones. El costo inicial lo vale a la larga, dado que la mayoría de las operaciones futuras van a ser rápidas y desconectado. De todos modos, en algunas situaciones, seria preferible crear un clon sin profundidad usando la opción `--depth`. Esto es mucho más rápido, pero el clon resultante tiene su funcionalidad reducida. === Proyectos Volátiles === Git fue escrito para ser rápido respecto al tamaño de los cambios. Los humanos hacen pequeñas ediciones de versión a versión. Un bugfix de una línea acá, una nueva funcionalidad allá, comentarios retocados, y así en adelante. Pero si tus archivos son radicalmente diferentes en revisiones sucesivas, entonces en cada commit, tu historia crece necesariamente igual que tu proyecto entero. No hay nada que ningún sistema de control de versiones pueda hacer sobre esto, pero los usuarios standard de Git van a sufrir más, dado que las historias son clonadas. La razón por la que los cambios son tan grandes deberían ser examinadas. Tal vez los formatos de los archivos deberían ser cambiados, ediciones pequeñas deberían causar cambios menores en a lo sumo unos pocos archivos. O tal vez una base de datos o una solución de backup/archivado es lo que realmente se necesita, no un sistema de control de versiones. Por ejemplo, un el control de versiones es poco adecuado para administrar fotos tomadas periódicamente con una webcam. Si los archivos deben cambiar constantemente, y realmente se necesita que estén versionados, una posibilidad es usar Git de una forma centralizada. Uno puede crear clones sin profundidad, lo cual acarrea poco y nada de la historia del proyecto. Por supuesto, muchas herramientas de git no van a estar disponibles, y los arreglos deben ser enviados como patches. Esto probablement no sea problema, dado que no está claro por qué alguien quisiera un historial de archivos salvajemente inestables. Otro ejemplo es un proyecto que depende de firmware, que toma la forma de un archivo binario enorme. La historia de este firmware no es de interés para los usuarios, y las actualizaciones comprimen de forma insatisfactoria, por lo que las revisiones de firmware aumentarían el tamaño del repositorio de manera innecesaria. En este caso, el código fuente debe ser guardado en un repositorio de Git, y el archivo binario debe ser mantenido de forma separada. Para hacer la vida más fácil, uno puede distribuír un script que usa Git para clonar el código y rsync o un clon sin profundidad de Git para el firmware. === Contador Global === Algunos sistemas de control de versiones centralizados, mantienen un entero positivo que aumenta cuando un nuevo commit es aceptado. Git se refiere a los cambios por su hash, lo que es mejor en muchas circunstancias. Pero a algunas personas les gusta tener este entero a mano. Por suerte es fácil escribir scripts de forma que con cada update, el repositorio central de git incrementa un entero, tal vez en un tag, y lo asocia con el hash del último commit. Cada clon podría mantener esete contador, pero esto sería probablemente inútil, dado que solo el repositorio central y su contador son los que importan. === Subdirectorios Vacíos === Los subdirectorios vacíos no pueden ser administrados. Crea archivos dummy para evitar este problema. La implementación actual de Git, y no su diseño, es quien tiene la culpa de este problema. Con suerte, una vez que Git gane más tracción, más usuarios van a clamar por esta funcionalidad y va a ser implementada. === Commit Inicial === El estereotipo de un informático teórico cuenta desde 0, en lugar de desde 1. Lamentablemente, con respecto a los commit, Git no mantiene esta convención. Muchos comandos son poco amigables antes del commit inicial. Adicionalmente algunos casos borde deben ser manejados de forma especial, como hacer rebase de una rama con un commit inicial distinto. Git se beneficiaría al definir el commit cero: tan pronto como se construye un repositorio, HEAD debería ser un string conteniendo 20 bytes de 0. Este commit especial representa un árbol vacío, sin padre, que en algún momento es parte de todos los repositorios de Git. Entonces el correr git log, por ejemplo, informaría al usuario que no se han hecho commits aún, en lugar de salir con un error fatal. Algo similar pasaría con otras herramientas. Cada commit inicial es de forma implícita un descendiente de este commit cero. Lamentablemente igual hay algunos casos que presentan problemas. Si varias ramas con commits iniciales diferentes se mergean juntas, entonces un rebase del resultado requiere una buena cantidad de intervención manual. === Rarezas De La Interfaz === Para los commits A y B, el significado de las expresiones "A..B" y "A...B" depende de si el comando espera dos puntas o un rango. Ver *git help diff* y *git help rev-parse* gitmagic-20160304/es/branch.txt0000644000175000017500000002411112666307504015404 0ustar sbadiasbadia== Magia Con Los Branches == El hacer branches (ramificar) y merges (unir) de manera instantánea, son dos de las prestaciones más letales de Git. *Problema*: Factores externos necesitan inevitablemente de cambios de contexto. Un bug severo se manifiesta en la última versión sin previo aviso. El plazo para alguna prestación se acorta. Un desarrollador que tiene que ayudar en una sección indispensable del proyecto está por tomar licencia. En cualquier caso, debes soltar abruptamente lo que estás haciendo y enfocarte en una tarea completamente diferente. Interrumpir tu línea de pensamiento puede ser negativo para tu productividad, y cuanto más engorroso sea el cambiar contextos, mayor es la pérdida. Con los sistemas centralizados, debemos descargar una nueva copia. Los sistemas distribuídos se comportan mejor, dado que podemos clonar la versión deseada localmente. Pero el clonar igual implica copiar todo el directorio junto con toda la historia hasta el momento. Aunque Git reduce el costo de esta operación usando hard links y el compartir archivos, los archivos del proyecto deben ser recreados enteramente en el nuevo directorio. *Solución*: Git tiene una mejor herramienta para estas situaciones que es mucho más rápida y eficiente en tamaño que clonar *git branch*. Con esta palabra mágica, los archivos en tu directorio se transforman súbitamente de una versión en otra. Esta transformación puede hacer más que simplemente ir hacia atrás o adelante en la historia. Tus archivos pueden mutar desde la última versión lanzada, a la versión experimental, a la versión en desarrollo, a la versión de un amigo y así sucesivamente. === La Tecla Del Jefe === ¿Alguna vez jugaste uno de esos juegos donde con solo presionar un botón ("la tecla del jefe"), la pantalla inmediatamente muestra una hoja de cálculo o algo así? La idea es que si el jefe entra a la oficina mientras estás en el juego, lo puedes esconder rápidamente. En algún directorio: $ echo "Soy más inteligente que mi jefe" > miarchivo.txt $ git init $ git add . $ git commit -m "Commit inicial" Creamos un repositorio de Git que guarda un archivo de texto conteniendo un mensaje dado. Ahora escribe: $ git checkout -b jefe # nada parece cambiar luego de esto $ echo "Mi jefe es más inteligente que yo" > miarchivo.txt $ git commit -a -m "Otro commit" Parecería que sobreescribimos nuestro archivo y le hicimos commit. Pero es una ilusión. Escribe: $ git checkout master # cambia a la versión original del archivo y ¡presto! El archivo de texto es restaurado. Y si el jefe decide investigar este directorio, escribimos: $ git checkout jefe # cambia a la versión adecuada para los ojos del jefe Puedes cambiar entre ambas versiones del archivo cuantas veces quieras, y hacer commit en ambas de manera independiente. === Trabajo Sucio === [[branch]] Supongamos que estás trabajando en alguna prestación, y que por alguna razón, necesitas volver a una versión vieja y poner temporalmente algunos "print" para ver como funciona algo. Entonces: $ git commit -a $ git checkout HASH_SHA1 Ahora puedes agregar cualquier código temporal horrible por todos lados. Incluso puedes hacer commit de estos cambios. Cuando termines, $ git checkout master para volver a tu trabajo original. Observa que arrastrarás cualquier cambio del que no hayas hecho commit. ¿Que pasa si quisieras cambiar los cambios temporales? Fácil: $ git checkout -b sucio y haz commit antes de volver a la branch master. Cuando quieras volver a los cambios sucios, simplemente escribe: $ git checkout sucio Mencionamos este comando en un capítulo anterior, cuando discutíamos sobre cargar estados antiguos. Al fin podemos contar toda la historia:los archivos cambian al estado pedido, pero debemos dejar la branch master. Cualquier commit de aquí en adelante, llevan tus archivos por un nuevo camino, el podrá ser nombrado posteriormente. En otras palabras, luego de traer un estado viejo, Git automáticamente te pone en una nueva branch sin nombre, la cual puede ser nombrada y salvada con *git checkout -b*. === Arreglos Rápidos === Estás en medio de algo cuando te piden que dejes todo y soluciones un bug recién descubierto: $ git commit -a $ git checkout -b arreglos HASH_SHA1 Luego, una vez que solucionaste el bug: $ git commit -a -m "Bug arreglado" $ git push # al repositorio central $ git checkout master y continúa con el trabajo en tu tarea original. === Flujo De Trabajo Ininterrumpido === Algunos proyectos requieren que tu código sea evaluado antes de que puedas subirlo. Para hacer la vida más fácil para aquellos que revisan tu código, si tienes algún cambio grande para hacer, puedes partirlo en dos o mas partes, y hacer que cada parte sea evaluada por separado. ¿Que pasa si la segunda parte no puede ser escrita hasta que la primera sea aprobada y subida? En muchos sistemas de control de versiones, deberías enviar primero el código a los evaluadores, y luego esperar hasta que esté aprobado antes de empezar con la segunda parte. En realidad, eso no es del todo cierto, pero en estos sistemas, editar la Parte II antes de subir la Parte I involucra sufrimiento e infortunio. En Git, los branches y merges son indoloros (un termino técnico que significa rápidos y locales). Entonces, luego de que hayas hecho commit de la primera parte y la hayas enviado a ser revisada: $ git checkout -b parte2 Luego, escribe la segunda parte del gran cambio sin esperar a que la primera sea aceptada. Cuando la primera parte sea aprobada y subida, $ git checkout master $ git merge parte2 $ git branch -d parte2 # ya no se necesita esta branch y la segunda parte del cambio está lista para la evaluación. ¡Pero esperen! ¿Qué pasa si no fuera tan simple? Digamos que tuviste un error en la primera parte, el cual hay que corregir antes de subir los cambios. ¡No hay problema! Primero, vuelve a la branch master usando $ git checkout master Soluciona el error en la primera parte del cambio y espera que sea aprobado. Si no lo es, simplemente repite este paso. Probablemente quieras hacer un merge de la versión arreglada de la Parte I con la Parte II: $ git checkout parte2 $ git merge master Ahora es igual que lo anterior. Una vez que la primera parte sea aprobada: $ git checkout master $ git merge parte2 $ git branch -d parte2 y nuevamente, la segunda parte está lista para ser revisada. Es fácil extender este truco para cualquier cantidad de partes. === Reorganizando Una Mezcla === Quizás quieras trabajar en todos los aspectos de un proyecto sobre la misma branch. Quieres dejar los trabajos-en-progreso para ti y quieres que otros vean tus commits solo cuando han sido pulcramente organizados. Inicia un par de branches: $ git checkout -b prolijo $ git checkout -b mezcla A continuación, trabaja en lo que sea: soluciona bugs, agrega prestaciones, agrega código temporal o lo que quieras, haciendo commits seguidos a medida que avanzas. Entonces: $ git checkout prolijo $ git cherry-pick HASH_SHA1 aplica un commit dado a la branch "prolijo". Con cherry-picks apropiados, puedes construir una rama que contenga solo el código permanente, y los commits relacionados juntos en un grupo. === Administrando branches === Lista todas las branches escribiendo: $ git branch Siempre hay una branch llamada "master", y es en la que comienzas por defecto. Algunos aconsejan dejar la rama "master" sin tocar y el crear nuevas branches para tus propios cambios. Las opciones *-d* y *-m* te permiten borrar y mover (renombrar) branches. Mira en *git help branch* La branch "master" es una convención útil. Otros pueden asumir que tu repositorio tiene una branch con este nombre, y que contiene la versión oficial del proyecto. Puedes renombrar o destruir la branch "master", pero también podrías respetar esta costumbre. === Branches Temporales === Después de un rato puedes notar que estás creando branches de corta vida de manera frecuente por razones similares: cada branch sirve simplemente para salvar el estado actual y permitirte saltar a un estado anterior para solucionar un bug de alta prioridad o algo. Es análogo a cambiar el canal de la TV temporalmente, para ver que otra cosa están dando. Pero en lugar de apretar un par de botones, tienes que crear, hacer checkout y eliminar branches y commits temporales. Por suerte, Git tiene un atajo que es tan conveniente como un control remoto de TV: $ git stash Esto guarda el estado actual en un lugar temporal (un 'stash') y restaura el estado anterior. Tu directorio de trabajo se ve idéntico a como estaba antes de que comenzaras a editar, y puedes solucionar bugs, traer cambios desde otros repositorios, etc. Cuando quieras volver a los cambios del stash, escribe: $ git stash apply # Puedes necesitar corregir conflictos Puedes tener varios stashes, y manipularlos de varias maneras. Mira *git help stash*. Como es de imaginar, Git mantiene branches de manera interna para lograr este truco mágico. === Trabaja como quieras === Aplicaciones como http://www.mozilla.com/[Mozilla Firefox] permiten tener varias pestañas y ventanas abiertas. Cambiar de pestaña te da diferente contenido en la misma ventana. Los branches en git son como pestañas para tu directorio de trabajo. Siguiendo esta analogía, el clonar es como abrir una nueva ventana. La posibilidad de ambas cosas es lo que mejora la experiencia del usuario. En un nivel más alto, varios window managers en Linux soportan múltiples escritorios. Usar branches en Git es similar a cambiar a un escritorio diferente, mientras clonar es similar a conectar otro monitor para ganar un nuevo escritorio. Otro ejemplo es el programa http://www.gnu.org/software/screen/[*screen*]. Esta joya permite crear, destruir e intercambiar entre varias sesiones de terminal sobre la misma terminal. En lugar de abrir terminales nuevas (clone), puedes usar la misma si ejecutas *screen* (branch). De hecho, puedes hacer mucho más con *screen*, pero eso es un asunto para otro manual. Usar clone, branch y merge, es rápido y local en Git, animándote a usar la combinación que más te favorezca. Git te permite trabajar exactamente como prefieras. gitmagic-20160304/es/secrets.txt0000644000175000017500000003220012666307504015615 0ustar sbadiasbadia== Secretos Revelados == Tomemos un vistazo bajo la mesa y expliquemos cómo realiza sus milagros. No escatimare sobre los detalles. Para descripciones detalladas referirse a http://schacon.github.com/git/user-manual.html[el manual de usuario]. === Invisibilidad === ¿Cómo puede ser Git tan discreto? Aparte de los "commit" y "merge" ocasionales, usted puede trabajar inconscientemente de que un control de versiones existe. Esto sucede hasta que usted lo necesita, y esto es cuando esta agradecido de que Git este viendo por usted todo el tiempo. Otros sistemas de control de versiones te fuerzan a luchar constantemente con cinta roja y burocracia. Los permisos de archivos podría ser de solo lectura a menos que usted explícitamente le diga a un servidor central cuales archivos intenta editar. Los comandos más básicos podrían retardarse al punto de arrastrarse cuando el número de usuarios se incrementa. El trabajo se paraliza cuando la red o el servidor central deja de funcionar. En contraste, Git simplemente mantiene la historia de su proyecto en el directorio '.git' ubicado en su directorio de trabajo. Ésta es su propia copia de la historia, de esta manera usted puede trabajar desconectado hasta que desee comunicarse con otros. Usted tiene control total del destino de sus archivos ya que Git puede fácilmente recrear un estado guardado de '.git' en cualquier momento. === Integridad === Muchas personas asocian criptografía con manterner la información secreta, pero otro objetivo importante es mantener la información segura. El uso apropiado de las funciones "hash" en criptografía puede prevenir corrupción de datos accidental o intencional. Un hash SHA1 puede pensarse como un identificador numérico único de 160-bit para cualquier linea de caracteres que usted pueda encontrar en su vida. Pero mas que eso: cualquier linea de caracteres que cualquier ser humano vaya a usar en muchas vidas. Como un "hash" SHA1 es una linea de "bytes", podemos dividir lineas de "bytes" que contienen otros "hash". Esta simple observación es sorpresivamente útil: buscar 'cadenas de "hash"'. Mas adelante veremos como Git lo utiliza para garantizar eficientemente la integridad de datos. Brevemente, Git mantiene sus datos en el subdirectorio '.git/objects', donde en lugar de archivos normales, usted encontrara solo identificadores. Al usar identificadores como nombres de archivos, asi como algunos trucos de bloqueo de archivos y sellos de tiempo, Git transforma cualquier humilde sistema de archivos en una eficiente y robusta base de datos. === Inteligencia === ¿Cómo sabe Git que usted renombró un archivo, incluso pensando que usted nunca menciono el hecho explícitamente? Seguramente, usted podria haber ejecutado *git mv*, pero eso es exactamente lo mismo a *git rm* seguido de *git add*. Git heuristicamente averigua los cambios de nombre y copias entre versiones sucesivas. ¡De hecho, puede detectar pedazos de codigo que estan siendo movidos o copiados entre archivos! Al pensar que no puede cubrir todos los casos, realiza un trabajo decente, y esta caracteristica esta siendo mejorada constantemente. Si falla al trabajar por usted, intente habilitar deteccion de copias mas caras y considere actualizar. === Indexacion === Para cada archivo rastreado, Git guarda informacion como su tamano, fecha de creacion y ultima fecha de modificacion en un archivo conocido como 'index'. Para determinar cuando un archivo ha cambiado, Git compara su estado actual con lo que estan acumulados en el indice. Si son iguales, entonces Git omite leer el archivo nuevamente. Partiendo del hecho de que las llamadas de estado son considerablemente mas rapidas que leer archivos, si usted edita unicamente algunos archivos, Git puede actualizar su estado en casi nada de tiempo. Hemos insistido anteriormente que el indice es un area de escenificacion. ¿Porque un monton de estados de archivo es un area de escenificacion? Porque el comando "add" pone archivos en la based de datos de Git y actualiza los estados, mientras que el comando "commit", sin opciones, crea una actualizacion basada unicamente en estos estados y los archivos que ya estan en la base de datos. === Los origenes de Git === Esto http://lkml.org/lkml/2005/4/6/121[ anuncio: Lista de Correos de "Linux Kernel"] Describe la cadena de eventos que llevaron a Git. El hilo entero es un fascinante sitio para los historiadores de Git. === La Base de Datos de Objetos === Toda version de sus datos is mantenida en la "Base de Datos de Objetos", la cual vive en el subdirectorio 'git./objects'; los otros residentes de '.git' mantienen menos datos: el indice, nombres de ramas, etiquetas, opciones de configuracion, logs, la localizacion actual del primer "commit", y asi sucesivamente. La Base de Datos de Objetos is elemental pero elegante, y la fuente del poder de Git. Cada archivo que se encuentre en '.git/objects' es un 'objeto'. Existen 3 tipos de objetos que nos concierne: objetos 'blob', objetos 'tree', y objetos 'commit'. === Blobs === Primero, un truco magico. Elija un archivo, cualquier archivo. En un directorio vacio: $ echo sweet > NOMBRE_DE_ARCHIVO $ git init $ git add . $ find .git/objects -type f Usted podra ver +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. ¿Cómo se esto sin saber el nombre del archivo? Es porque el "hash" SHA1 de: "blob" SP "6" NUL "sweet" LF es aa823728ea7d592acc69b36875a482cdf3fd5c8d, donde SP es un espacio, NUL es un "byte" zero y LF es un avance de linea. Usted puede verificar esto escribiendo: $ printf "blob 6\000sweet\n" | sha1sum Git es 'contenido-direccionable': los archivos no son almacenados de acuerdo con su nombre, sino mas por el "hash" de la informacion que contiene, dentro de un archivo llamamos al 'objeto "blob"'. Podemos pensar de un "hash" como el identificador del contenido de un archivo, asi que en un sentido estamos buscando archivos por su contenido. El "blob" inicial es meramente un encabezado que consiste en el typo del objeto y su tamaño en "bytes"; lo que simplifica la contabilidad interna. De esta manera yo puedo predecir fácilmente lo que usted vera. El nombre del archivo es irrelevante: solo la información interna es usada para construir el 'objeto "blob"'. Usted podría estarse preguntando qué sucede con archivos idénticos. Intente agregar copias de su archivo, con cualquier cosa de nombre. El contenido de +.git/objects+ se queda de la misma manera sin importar cuantos agregue. Git guarda la información solo una vez. Por cierto, los archivos ubicados en +.git/objects+ estan comprimidos con zlib asi que usted no debería poder verlos directamente. Filtrelos a través de http://www.zlib.net/zpipe.c[zpipe -d], o digite: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d lo que bellamente imprime el objeto dado. === Árboles === ¿Pero dónde están los nombres de archivo? Ellos deberían estar almacenados en algún lugar en algún organizador. Git toma su turno para poner los nombres de archivo durante un "commit": $ git commit # Escribe algún mensaje. $ find .git/objects -type f Ahora debería poder ver 3 objetos. Esta vez no puedo decirte qué son esos 2 nuevos archivos, esto debende parcialmente del nombre de archivo que escogió. Procederemos asumiendo que eligió 'rose'. Si usted no lo hizo, podría reescribir la historia para hacer parecer que usted lo hizo: $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose' $ find .git/objects -type f Ahora debería poder ver el archivo +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, porque este es el SHA1 "hash" de su contenido: "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Verifique que este archivo contiene verdaderamente lo anterior escribiendo: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch Con zpipe, es fácil verificar el hash: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum Las verficaciones de "hash" son mas difíciles utilizando cat-file porque su salida contiene más que la fuente del objeto del archivo descomprimido. Este archivo es un objeto "tree": una lista de tuplas de un tipo de archivo, un nombre de archivo, y un "hash". En nuestro ejemplo, el tipo de archivo es 100644, lo cual significa que 'rose' es un archivo normal, y el "hash" es el objeto "blob" que contiene los contenidos de 'rose'. Otro posible tipo de archivo son los ejecutables, enlaces simbólicos o los directorios. En el último caso, los punteros "hash" a un objeto 'tree'. Si usted ejecuta "filter-branch", obtendrá objetos viejos que ya no necesita. Aunque serán desechados automáticamente una vez que su periodo de gracia expire, nosotros los borraremos ahora para hacer nuestro ejemplo de prueba más fácil de seguir: $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune Para proyectos reales típicamente usted debería evitar comandos como este, ya que está destruyendo respaldos. Si usted quiere un repositorio limpio, usualmente es mejor crear un clon nuevo. También, tenga cuidado cuando está manipulando directamente +.git+: ¿qué pasaría si un proceso Git está corriendo al mismo tiempo, ó un corte eléctrico ocurre? En general, las referencias deberían ser eliminadas con *git update-ref -d*, pensando que usualmente es más seguro remover +refs/original+ a mano. === "Commits" === Hemos explicado 2 de 3 objetos. El tercero es un objeto '"commit"'. Sus contenidos dependen del mensaje del "commit" así como de la fecha en el que fué creado. Para contrastar lo que tenemos aquí, deberemos torcerlo un poco: $ git commit --amend -m Shakespeare # Change the commit message. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Rig timestamps and authors. $ find .git/objects -type f +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice 1234567890 -0800" LF "committer Bob 1234567890 -0800" LF LF "Shakespeare" LF Como antes, usted puede correr zpipe ó cat-file para que vea por usted mismo. Este es el primer "commit", así que no existe un "commit" padre, pero "commit" sucesivos} siempre van a contener al menos una línea identificando el "commit" padre. === Sin Distinción de la Magia === Los secretos de Git parecen muy simples. Parece ser que usted podría unir algunos comandos y agregar una parte de código C para cocinar algo en cuestión de horas: una mezcla de operaciones básicas con archivos de sistema y SHA1 "hashing", adornado con archivos "lock" y "fsyncs" para robustez. De hecho, esto describe con exactitud las versiones anteriores de Git. Sin embargo, fuera de ingeniosos trucos de empaque para salvar espacio, e ingeniosos trucos de indexación para salvar tiempo, nosotros ahora sabemos definitivamente cómo Git cambia los sistemas de archivo en una base de datos perfecta para el control de versiones. Por ejemplo, si cualquier archivo dentro de la base de datos es corrompido por un error de disco, entonces su "hash" nunca más podrá ser emparejado, alertándonos del problema. Al crear un "hash" de otros "hash" de otros objetos, mantenemos la integridad a todos los niveles. Los "commit" son atómicos, eso es, un "commit" nunca puede guardar solamente partes de cambios: nosotros podemos únicamente calcular el "hash" de un "commit" y guardarlo en la base de datos después de que ya hemos guardado todos los árboles relevantes, los "blob" y los "commit" padres. La base de datos de objetos es inmune a interrupciones inesperadas como cortes de electricidad. Hemos derrotado inclusive al más tortuoso adversario. Suponga que alguien intenta modificar furtivamente los contenidos de un archivo en una versión anterior de un proyecto. Para mantener la base de datos de objetos sana, ellos deben cambiar también el "hash" del objeto "blob" correspondiente porque ahora es una cadena de bytes distinta. Esto significa que además tendrán que cambiar el "hash" de cualquier árbol de objetos referenciando el archivo, y por defecto cambiar todos los "hash" de todos los descendientes del árbol de ese "commit". Además de todos los "hash" de todos los descendientes de ese "commit".Esto implica que el "hash" de la cabeza oficial difiere a la del repositorio erróneo. Para seguir el curso de los "hash" que difieren podemos localizar el archivo mutilado, así como el "commit" en donde fue corrompido inicialmente. En corto tiempo, mientras que los 20 "byte" representando el último "commit" seguro, es imposible de manosear en un repositorio de Git. ¿Qué sucede con las famosas características de Git? ¿Las ramas? ¿Las mezclas? ¿Los tags? Simples detalles. La cabeza actual es mantenida en el archivo +.git/HEAD+, la cual contiene un "hash" de un objeto "commit". El "hash" es actualizado durante un "commit" así como cualquier otro comando. Las ramas son casi lo mismo: son archivos en +.git/refs/heads+. Las etiquetas también: se encuentran en +.git/refs/tags+ pero son actualizadas por un juego de comandos distintos. gitmagic-20160304/es/spanish.txt0000644000175000017500000000112212666307504015611 0ustar sbadiasbadia=== Last revision in which the translation was synchronized === preface.txt 09751f37469d32da649b1c64862023820e0d0499 intro.txt 09751f37469d32da649b1c64862023820e0d0499 basic.txt 09751f37469d32da649b1c64862023820e0d0499 clone.txt 46c05532226868b8b3b121d9320c0150491fd181 branch.txt 46c05532226868b8b3b121d9320c0150491fd181 multiplayer.txt history.txt b17fbe91cafc00866c8c82d705920cd6c5a476d3 grandmaster.txt b17fbe91cafc00866c8c82d705920cd6c5a476d3 secrets.txt drawbacks.txt b17fbe91cafc00866c8c82d705920cd6c5a476d3 translate.txt gitmagic-20160304/es/multiplayer.txt0000644000175000017500000002254612666307504016530 0ustar sbadiasbadia== Git Multijugador == Inicialmente usaba Git en un proyecto privado donde yo era el único desarrollador. Entre los comandos relacionados a la naturaleza distribuida de Git, sólo necesitaba *pull* y *clone* así yo podía mantener el mismo proyecto en diferentes lugares. Más tarde quise publicar mi código con Git, e incluir cambios de los contribuyentes. Tuve que aprender a administrar proyectos con múltiples desarrolladores de todo el mundo. Afortunadamente, esta es la fortaleza de Git, y podría decirse que su razón de ser. === ¿Quién Soy Yo? === Cada commit tiene un nombre de autor y email, los cuales son mostrados por *git log*. Por defecto, Git usa la configuración del sistema para rellenar estos campos. Para decirle explícitamente, escribe: $ git config --global user.name "John Doe" $ git config --global user.email johndoe@ejemplo.com Omite el parámetro *--global* si quieres que estas opciones sólo sean aplicadas en el repositorio actual. === Git Sobre SSH, HTTP === Supón que tienes acceso SSH a un servidor web, pero Git no está instalado. Aunque es menos eficiente que su protocolo nativo, Git se puede comunicar por HTTP. Descarga, compila e instala Git en tu cuenta, y crea un repositorio en tu directorio web: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update En versiones antiguas de Git, el comando cp falla y debes ejecutar: $ chmod a+x hooks/post-update Ahora tú puedes publicar tus últimas ediciones via SSH desde cualquier clon: $ git push servidor.web:/ruta/al/proyecto.git master y cualquiera puede obtener tu proyecto con: $ git clone http://servidor.web/proyecto.git === Git Sobre Cualquier Cosa === ¿Quieres sincronizar repositorios sin servidores, o incluso sin conexión de red? ¿Necesitas improvisar durante una emergencia? Hemos visto cómo <>. Podríamos transportar tales archivos de ida y vuelta para enviar repositorios git sobre cualquier medio, pero una herramienta más eficiente es *git bundle*. El emisor crea un 'bundle' (paquete): $ git bundle create algunarchivo HEAD Luego envía el paquete, +algunarchivo+, a la otra parte de alguna forma: email, pendrive, una impresión *xxd* y un escáner OCR, leyendo bits a través del teléfono, señales de humo, etc. El receptor recupera los commits del paquete escribiendo: $ git pull algunarchivo El receptor puede incluso hacer esto desde un repositorio vacío. A pesar de su tamaño, +algunarchivo+ contiene el repositorio git original completo. En proyectos más grandes, elimina la basura empaquetando sólo los cambios de los que carece el otro repositorio. Por ejemplo, supón que el commit ``1b6d...'' es el commit más reciente compartido por ambas partes: $ git bundle create algunarchivo HEAD ^1b6d Si se hace a menudo, uno puede olvidar fácilmente cual commit fue el último enviado. La página de ayuda sugiere usar tags para resolver esto. Es decir, después de que envías un paquete, escribe: $ git tag -f ultimopaquete HEAD y crea nuevos paquetes de actualización con: $ git bundle create nuevopaquete HEAD ^ultimopaquete === Parches: La Moneda Global === Los parches son representaciones de texto de tus cambios que pueden ser fácilmente entendidos por computadores y humanos por igual. Esto les da una calidad universal. Puedes enviar por email un parche a los desarrolladores sin importar qué sistema de control de versiones estén usando. Mientras tu audiencia pueda leer su email, ella puede ver tus ediciones. Similarmente, por tu lado, todo lo que requieres es una cuenta de correo: no hay necesidad de crear un repositorio Git en línea. Recuerda del primer capítulo: $ git diff 1b6d > my.patch obtiene un parche que puede se pegado en un email para discusión. En un repositorio Git, escribe: $ git apply < my.patch para aplicar el parche. En un ambiente más formal, cuando los nombres de los autores y quizás las firmas deben ser guardadas, genera los parches correspondientes pasados un cierto punto escribiendo: $ git format-patch 1b6d Los archivos resultantes pueden ser enviados a *git-send-email*, o enviado a mano. También puedes especificar un rango de commits: $ git format-patch 1b6d..HEAD^^ En el extremo receptor, guarda el mensaje a un archivo, luego escribe: $ git am < email.txt Esto aplica el parche entrante y también crea el commit, incluyendo información tal como el autor. Con un cliente de correo, puedes necesitar hacer clic en un botón para ver el mensaje en su forma original antes de guardar el parche a un archivo. Hay algunas ligeras diferencias para los clientes basados en casillas de correo, pero si tú usas uno de esos, ¡eres probablemente la persona que puede deducirlo fácilmente sin leer tutoriales! === Lo Siento, Nos Hemos Movido === Después de clonar un repositorio, correr *git push* o *git pull* hará push hacia o pull desde la URL original. ¿Cómo Git hace esto? El secreto está en las opciones de configuración creadas con el clone. Echemos un vistazo: $ git config --list La opción +remote.origin.url+ controla la URL fuente; ``origin'' es un alias dado al repositorio fuente. Al igual que con la convención de la rama ``master'', podemos cambiar o borrar este alias, pero usualmente no hay razón para hacerlo. Si el repositorio original se mueve, podemos actualizar la URL con: $ git config remote.origin.url git://nueva.url/proyecto.git La opción +branch.master.merge+ especifica la rama remota por defecto en un *git pull*. Durante la clonación inicial, se configura a la rama actual del repositorio fuente, incluso si el HEAD del repositorio fuente se mueve posteriormente a una rama diferente, más tarde un pull va a seguir fielmente la rama original. Esta opción sólo se aplica al repositorio en la primera vez que se clona, que es guardado en la opción +branch.master.remote+. Si tiramos desde otros repositorios debemos decirle explícitamente que rama queremos: $ git pull git://example.com/other.git master El ejemplo de más arriba explica por qué algunos de nuestros ejemplos anteriores de push y pull no tenían argumentos. === Ramas Remotas === Cuando clonas un repositorio, también clonas todas sus ramas. Tú puedes no haber notado esto porque Git los esconde: debes consultar por ellos específicamente. Esto evita que las ramas en el repositorio remoto interfieran con tus ramas, y también hace a Git más fácil para los principiantes. Lista las ramas remotas con: $ git branch -r Deberías ver algo como esto: origin/HEAD origin/master origin/experimental Estas representan ramas y el HEAD del repositorio remoto, y pueden ser usados en los comandos regulares de Git. Por ejemplo, supón que has hecho muchos commits, y deseas compararlos con la última versión traída. Tú podrías buscar en los registros (logs) por el hash SHA1 adecuado, pero es mucho más fácil escribir: $ git diff origin/HEAD O puedes ver lo que ha sucedido con la rama ``experimental'': $ git log origin/experimental === Múltiples Remotes === Supón que otros dos desarrolladores están trabajando en nuestro proyecto, y queremos mantener pestañas en ambos. Podemos seguir más de un repositorio a la vez con: $ git remote add otro git://ejemplo.com/algun_repositorio.git $ git pull otro alguna_rama Ahora hemos mezclado una rama desde el segundo repositorio, y tenemos acceso fácil a todas las ramas de todos los repositorios: $ git diff origin/experimental^ otro/alguna_rama~5 Pero, ¿qué pasa si queremos comparar sus cambios sin afectar nuestro propio trabajo? En otras palabras, queremos examinar las ramas evitando que sus cambios invadan nuestro directorio de trabajo. Entonces, en vez de pull, ejecuta: $ git fetch # Recuperamos desde el origen, por defecto. $ git fetch otro # Recuperamos lo del segundo programador. Esto sólo obtiene historias. Aunque el directorio de trabajo permanece intacto, podemos referirnos a cualquier rama de cualquier repositorio en un comando Git ya que ahora poseemos una copia local. Recuerda que detrás de las cámaras, un pull es simplemente un *fetch* luego *merge*. Usualmente hacemos *pull* porque queremos mezclar el último commit después de un fetch; esta situación es una excepción notable. Vea *git help remote* para saber cómo remover repositorios, ignorar ciertas ramas, y más. === Mis Preferencias === En mis proyectos, me gusta que los contribuyentes preparen los repositorios desde los cuales voy a hacer pull. Algunos servicios de hosting Git te permiten hospedar tu propia bifurcación de un proyecto con el clic de un botón. Después de que obtengo un árbol, uso comandos Git para navegar y examinar los cambios, los que idealmente están bien organizados y bien descritos. Mezclo mis propios cambios, y quizás hago más ediciones. Una vez satisfecho, los empujo al repositorio principal. Aunque rara vez recibo contribuciones, creo que este enfoque escala bien. Vea http://torvalds-family.blogspot.com/ncr/2009/06/happiness-is-warm-scm.html[esta entrada de blog por Linus Torvalds]. Permanecer en el mundo Git es ligeramente más conveniente que parchar archivos, dado que me ahorra convertirlos a commits de Git. Además, Git maneja detalles como grabar el nombre y dirección de email del autor, así como la hora y fecha, y le pide al autor describir sus propios cambios. gitmagic-20160304/es/preface.txt0000644000175000017500000000613712666307504015564 0ustar sbadiasbadia= Git Magic = Ben Lynn August 2007 == Prólogo == http://git.or.cz/[Git] es la navaja suiza del control de versiones. Una herramienta de control de revisiones confiable, versátil y multipropósito, que por su extraordinaria flexibilidad es complicada de aprender, y más aún de dominar. Estoy documentando lo que he aprendido hasta ahora en estas páginas, porque inicialmente tuve dificultades para comprender http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[el manual de usuario de Git]. Tal como observó Arthur C. Clarke, cualquier tecnología suficientemente avanzada, es indistinguible de la magia. Este es un gran modo de acercarse a Git: los novatos pueden ignorar su funcionamiento interno, y ver a Git como un artefacto que puede asombrar a los amigos y enfurecer a los enemigos con sus maravillosas habilidades. En lugar de ser detallados, proveemos instrucciones generales para efectos particulares. Luego de un uso reiterado, gradualmente irás entendiendo como funciona cada truco, y como adaptar las recetas a tus necesidades. .Otras ediciones - http://docs.google.com/View?id=dfwthj68_675gz3bw8kj[Traducción al chino]: por JunJie, Meng y JiangWei. - link:book.html[Una única página]: HTML simple, sin CSS. - link:book.pdf[Archivo PDF]: Listo para imprimir. - http://packages.debian.org/search?searchon=names&keywords=gitmagic[Paquete gitmagic para Debian]: Consigue una copia rápida y local de este sitio. http://packages.ubuntu.com/jaunty/gitmagic[Paquete para Ubuntu (Jaunty Jackalope)] también disponible. Útil http://csdcf.stanford.edu/status/[cuando este servidor está offline para mantenimiento]. === Gracias! === Agradezco a Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar y Frode Aannevik por sugerencias y mejoras. Gracias a Daniel Baumann por crear y mantener el paquete para Debian. También gracias a JunJie, Meng y JiangWei por la traducción al chino. [Si me olvidé de tí, por favor recuérdamelo, porque suelo olvidarme de actualizar esta sección] Estoy muy agradecido por todos los que me han dado apoyo y elogios. Me gustaría que este fuera un libro real impreso, para poder citar sus generosas palabras en la tapa a modo de promoción. Hablando en serio, aprecio enormemente cada mensaje. El leerlos siempre ilumina mi ánimo. === Licencia === Esta guía se publica bajo la http://www.gnu.org/licenses/gpl-3.0.html[GNU General Public License versión 3]. Naturalmente, los fuentes se guardan en un repositorio Git, y pueden ser obtenidos escribiendo: $ git clone git://repo.or.cz/gitmagic.git # Crea el directorio "gitmagic". Ver debajo por otros mirrors. === Hosting Git gratuito === - http://repo.or.cz/[http://repo.or.cz/] hospeda proyectos gratuitos, http://repo.or.cz/w/gitmagic.git[incluyendo esta guía]. - http://gitorious.org/[http://gitorious.org/] es un sitio que apunta al hosting de proyectos open-source. - http://github.com/[http://github.com/] hospeda proyectos open-source gratis, http://github.com/blynn/gitmagic/tree/master[incluyendo esta guía], y proyectos privados por una cuota. gitmagic-20160304/es/basic.txt0000644000175000017500000001577112666307504015244 0ustar sbadiasbadia== Trucos Básicos == En lugar de sumergirte en un mar de comandos de Git, usa estos ejemplos elementales para mojarte los pies. A pesar de sus simplicidad, todos son útiles. De hecho, en mis primeros meses con Git nunca fui más allá del material en este capítulo. === Guardando Estados === Estás a punto de intentar algo drástico? Antes de hacerlo, toma una instantánea de todos los archivos en el directorio actual con: $ git init $ git add . $ git commit -m "Mi primer respaldo" Ahora, si tu edición se vuelve irrecuperable, ejecuta: $ git reset --hard para volver a donde estabas. Para volver a salvar el estado: $ git commit -a -m "Otro respaldo" ==== Agrega, Elimina, Renombra ==== El comando anterior solo seguirá la pista de los archivos que estaban presentes la primera vez que ejecutaste *git add*. Si añades nuevos archivos o subdirectorios, deberás decirle a Git: $ git add ARCHIVOSNUEVOS... De manera similar, si quieres que Git se olvide de determinados archivos, porque (por ejemplo) los borraste: $ git rm ARCHIVOSVIEJOS... Renombrar un archivo es lo mismo que eliminar el nombre anterior y agregar el nuevo. También puedes usar *git mv* que tiene la misma sintaxis que el comando *mv*. Por ejemplo: $ git mv ARCHIVOVIEJO ARCHIVONUEVO === Deshacer/Rehacer Avanzado === Algunas veces solo quieres ir hacia atrás y olvidarte de todos los cambios a partir de cierto punto, porque estaban todos mal. Entonces: $ git log te muestra una lista de commits recientes, y sus hashes SHA1. A continuación, escribe: $ git reset --hard SHA1_HASH para recuperar el estado de un commit dado, y borrar para siempre cualquier recuerdo de commits más nuevos. Otras veces, quieres saltar a un estado anterior temporalmente. En ese caso escribe: $ git checkout SHA1_HASH Esto te lleva atrás en el tiempo, sin tocar los commits más nuevos. Sin embargo, como en los viajes en el tiempo de las películas de ciencia ficción, estarás en una realidad alternativa, porque tus acciones fueron diferentes a las de la primera vez. Esta realidad alternativa se llama 'branch' (rama), y <>. Por ahora solo recuerda que $ git checkout master te llevará al presente. También, para que Git no se queje, siempre haz un commit o resetea tus cambios antes de ejecutar checkout. Para retomar la analogía de los videojuegos: - *`git reset \--hard`*: carga un juego viejo y borra todos los que son mas nuevos que el que acabas de cargar. - *`git checkout`*: carga un juego viejo, pero si continúas jugando, el estado del juego se desviará de los juegos que salvaste la primera vez. Cualquier partida nueva que guardes, terminará en una branch separada, representando la realidad alternativa a la que entraste. <> Puedes elegir el restaurar solo archivos o directorios en particular, al agregarlos al final del comando: $ git checkout SHA1_HASH algun.archivo otro.archivo Ten cuidado, esta forma de *checkout* puede sobreescribir archivos sin avisar. Para prevenir accidentes, haz commit antes de ejecutar cualquier comando de checkout, especialmente cuando estás aprendiendo a usar Git. En general, cuando te sientas inseguro del resultado de una operación, sea o no de Git, ejecuta antes *git commit -a*. ¿No te gusta cortar y pegar hashes? Entonces usa: $ git checkout :/"Mi primer r" para saltar al commit que comienza con el mensaje dado. También puedes pedir el 5to estado hacia atrás: $ git checkout master~5 ==== Revirtiendo ==== En una corte, los eventos pueden ser eliminados del registro. Igualmente, puedes elegir commits específicos para deshacer. $ git commit -a $ git revert SHA1_HASH va a deshacer solo el commit con el hash dado. Ejecutar *git log* revela que el revert es registrado como un nuevo commit. === Descargando Archivos === Obtén una copia de un proyecto administrado por git escribiendo: $ git clone git://servidor/ruta/a/los/archivos Por ejemplo, para bajar todos los archivos que usé para crear este sitio: $ git clone git://git.or.cz/gitmagic.git Pronto tendremos más para decir acerca del comando *clone*. === Lo Más Nuevo === Si ya descargaste una copia de un proyecto usando *git clone*, puedes actualizarte a la última versión con: $ git pull === Publicación Al Instante === Imagina que has escrito un script que te gustaría compartir con otros. Puedes decirles que simplemente lo bajen de tu computadora, pero si lo hacen mientras estás haciendo una modificación, pueden terminar en problemas. Es por esto que existen los ciclos de desarrollo. Los programadores pueden trabajar en un proyecto de manera frecuente, pero solo hacen público el código cuando consideran que es presentable. Para hacer esto con Git, en el directorio donde guardas tu script: $ git init $ git add . $ git commit -m "Primer lanzamiento" Entonces puedes decirle a tus usuarios que ejecuten: $ git clone tu.maquina:/ruta/al/script para descargar tu script. Esto asume que tienen acceso por ssh. Si no es así, ejecuta *git daemon* y dile a tus usuarios que usen: $ git clone git://tu.maquina/ruta/al/script De ahora en más, cada vez que tu script esté listo para el lanzamiento, escribe: $ git commit -a -m "Siguiente lanzamiento" y tus usuarios puede actualizar su versión yendo al directorio que tiene tu script y ejecutando: $ git pull Tus usuarios nunca terminarán usando una versión de tu script que no quieres que vean. Obviamente este truco funciona para lo que sea, no solo scripts. === ¿Que es lo que hice? === Averigua que cambios hiciste desde el último commit con: $ git diff O desde ayer: $ git diff "@{yesterday}" O entre una versión en particular y 2 versiones hacia atrás: $ git diff SHA1_HASH "master~2" En cada caso la salida es un patch (parche) que puede ser aplicado con *git apply* Para ver cambios desde hace 2 semanas, puedes intentar: $ git whatchanged --since="2 weeks ago" Usualmente recorro la historia con http://sourceforge.net/projects/qgit[qgit] , dada su interfaz pulida y fotogénica, o http://jonas.nitro.dk/tig/[tig], una interfaz en modo texto que funciona bien a través conexiones lentas. Como alternativa, puedes instalar un servidor web, ejecutar *git instaweb* y utilizar cualquier navegador web. === Ejercicio === Siendo A, B, C, y D cuatro commits sucesivos, donde B es el mismo que A pero con algunos archivos eliminados. Queremos volver a agregar los archivos en D pero no en B. ¿Cómo podemos hacer esto? Hay por lo menos tres soluciones. Asumiendo que estamos en D: 1. La diferencia entre A y B son los archivos eliminados. Podemos crear un patch representando esta diferencia y aplicarlo: $ git diff B A | git apply 2. Como en A tenemos los archivos guardados, podemos recuperarlos : $ git checkout A ARCHIVOS... 3. Podemos ver el pasaje de A a B como un cambio que queremos deshacer: $ git revert B ¿Cuál alternativa es la mejor? Cualquiera que prefieras. Es fácil obtener lo que quieres con Git, y normalmente hay varias formas de hacerlo. gitmagic-20160304/es/grandmaster.txt0000644000175000017500000002644112666307504016466 0ustar sbadiasbadia== Gran Maestría en Git == Esta página con nombre pretencioso es el cajón donde dejar los trucos de Git no categorizados. === Lanzamientos de Código === Para mis proyectos, Git controla únicamente los ficheros que me gustaría archivar y enviar a los usuarios. Para crear un tarball del código fuente, ejecuto: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === Commit De Lo Que Cambió === Decirle a Git cuándo agregaste, eliminaste o renombraste archivos es complicado para ciertos proyectos. En cambio, puedes escribir: $ git add . $ git add -u Git va a mirar los archivos en el directorio actual y resolver los detalles por si mismo. En lugar del segundo comando add, corre `git commit -a` si estás en condiciones de hacer commit. Ver en *git help ignore* como especificar archivos que deberían ser ignorados. Puedes hacer lo de arriba en un único paso con: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove Las opciones *-z* y *-0* previenen efectos secundarios adversos de archivos que contienen caracteres extraños. Como este comando agrega archivos ignorados, podrías querer usar la opción `-x` or `-X`. === ¡Mi Commit Es Muy Grande! === ¿Postergaste hacer un commit por demasiado tiempo? ¿Estabas enfervorizado escribiendo código y te olvidaste del control de fuentes hasta ahora? ¿Hiciste una serie de cambios no relacionados, simplemente porque es tu estilo? No te preocupes, ejecuta: $ git add -p Por cada edición que hiciste, Git va a mostrar el pedazo de código que fue cambiado, y preguntar si debería ser parte del próximo commit. Contesta con "y" o "n". Hay otras opciones, como posponer la decisión; escribe "?" para saber más. Una vez satisfecho, escribe $ git commit para hacer un commit que solo contiene los cambios seleccionados (los cambios 'staged'). Asegúrate de omitir la opción *-a*, o Git va a poner todo lo editado en el commit. ¿Que pasa si editaste varios archivos en varios lugares? Revisar cada cambio uno por uno se vuelve frustrante y adormecedor. En este caso, usa *git add -i*, cuya interfaz es menos clara pero más flexible. Con solo presionar un par de teclas, puedes poner o sacar del 'stage' varios archivos a la vez, o revisar y seleccionar cambios solamente en archivos particulares. Como alternativa se puede usar *git commit --interactive*, el cual hace commit luego de que terminas. ==== Cambios en el 'stage' ==== Hasta el momento hemos evitado el famoso 'indice' de git, pero ahora debemos enfrentarlo para explicar lo de arriba. El indice es un área temporal de montaje. Git evita enviar datos directamente entre tu proyecto y su historia. En su lugar, Git primero escribe datos al índice, y luego copia los datos del índice a su destino final. Por ejemplo, *commit -a* es en realidad un proceso de 2 pasos. El primer paso pone una instantánea del estado actual de cada archivo administrado en el índice. El segundo paso graba de forma permanente esa instantánea que está en el índice. Un commit hecho sin *-a* solo efectúa el segundo paso, y solo tiene sentido luego de haber ejecutado comandos que de alguna forma alteran el índice, como *git add*. Usualmente podemos ignorar el índice y pretender que estamos leyendo y escribiendo directo en la historia. En esta ocasión, queremos un control más fino de lo que se escribe en la historia, y nos vemos forzados a manipular el índice. Guardamos una instantánea de algunos, pero no todos, de nuestros cambios en el índice, y luego grabamos de forma permanente esta instantánea cuidadosamente organizada. === No Pierdas La Cabeza === El tag HEAD (Cabeza) es como un cursor que normalmente apunta al último commit, avanzando con cada nuevo commit. Algunos comandos de Git te dejan moverlo. Por ejemplo: $ git reset HEAD~3 mueve el HEAD tres commits hacia atrás. Por lo tanto todos los comandos de Git ahora actúan como si no hubieras hecho esos últimos tres commits, mientras tus archivos permanecen en el presente. Ver la página de ayuda para algunas aplicaciones. ¿Como hago para volver al futuro? Los commits del pasado nada saben del futuro. Teniendo el SHA1 del HEAD original, hacemos: $ git reset SHA1 Pero supongamos que nunca lo anotaste. No te preocupes, para comandos como este, Git guarda el HEAD original como un tag llamado ORIG_HEAD, y puedes volver sano y salvo con: $ git reset ORIG_HEAD === Cazando Cabezas === Quizás ORIG_HEAD no es suficiente. Quizás acabas de descubrir que cometiste un error monumental y que hay que volver a un commit antiguo en una rama olvidada hace largo tiempo. Por defecto, Git guarda un commit por al menos 2 semanas, incluso si le ordenaste destruir la rama que lo contenía. El problema es encontrar el hash apropiado. Podrías mirar todos los hashes en `.git/objects` y usar prueba y error para encontrar el que buscas. Pero hay una forma mucho más fácil. Git guarda el hash de cada commit que hace en `.git/logs`. El subdirectorio `refs` contiene la historia de la actividad en todas las ramas, mientras que el archivo `HEAD` tiene cada hash que alguna vez ha tomado. Este último puede usarse para encontrar hashes de commits en branches que se han borrado de manera accidental. El comando reflog provee una interfaz amigable para estos logs. Prueba $ git reflog En lugar de cortar y pegar hashes del reflog, intenta: $ git checkout "@{10 minutes ago}" O prueba un checkout del 5to commit que visitaste hacia atrás: $ git checkout "@{5}" Ver la sección ``Specifying Revisions'' de *git help rev-parse* por mas datos. Podrías querer configurar un periodo de gracia mayor para los commits condenados. Por ejemplo: $ git config gc.pruneexpire "30 days" significa que un commmit eliminado se va a perder de forma permanente solo cuando hayan pasado 30 días y se ejecute *git gc*. También podrías querer deshabilitar invocaciones automáticas de *git gc*: $ git config gc.auto 0 en cuyo caso los commits solo serán borrados cuando ejecutes *git gc* de forma manual. === Construyendo sobre Git === Siguiendo la tradición UNIX, el diseño de Git permite ser fácilmente usado como un componente de bajo nivel de otros programas, como GUI e interfaces web, interfaces de linea de comandos alternativas, herramientas de manejo de patches, herramientas de importación y conversión, etc. De hecho, algunos de los comandos de Git son ellos mismos scripts parados sobre los hombros de gigantes. Con unos pocos ajustes, puedes personalizar Git para cubrir tus necesidades. Un truco simple es usar los alias incluidos en git para acortar los comandos usados de forma más frecuente: $ git config --global alias.co checkout $ git config --global --get-regexp alias # muestra los alias actuales alias.co checkout $ git co foo # igual a 'git checkout foo' Otro es imprimir la rama actual en el prompt, o en el título de la ventana. Usar $ git symbolic-ref HEAD muestra el nombre de la rama actual. En la práctica, es probable que quieras quitar el "refs/heads/" e ignorar los errores: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- El subdirectorio +contrib+ es la cueva de los tesoros de las herramientas hechas con Git. Con tiempo, algunas de ellas pueden ser promovidas a comandos oficiales. En Debian y Ubuntu, este directorio está en +/usr/share/doc/git-core/contrib+. Un residente popular es +workdir/git-new-workdir+. Usando symlinks inteligentes, este script crea un nuevo directorio de trabajo cuya historia es compartida con el repositorio original: $ git-new-workdir repositorio/existente nuevo/directorio El nuevo directorio y sus archivos interiores pueden ser vistos como un clon, excepto que como la historia es compartida, ambos árboles se mantienen sincronizados de forma automática. No hay necesidad de merges, push ni pull. === Acrobacias Peligrosas === En estos días, Git hace difícil que el usuario destruya datos de manera accidental. Pero si sabes lo que estás haciendo, puedes hacer caso omiso de las trabas de seguridad para los comandos comunes. *Checkout*: Los cambios no commiteados hacen que checkout falle. Para destruir tus cambios, y hacer checkout de un commit dado, usa la opción de forzar: $ git checkout -f HEAD^ Por otro lado, si especificas una ruta específica para hacer checkout, no hay chequeos de seguridad. Las rutas suministradas son sobre-escritas de forma silenciosa. Hay que tener cuidado al usar checkout de esta forma: *Reset*: Reset también falla en presencia de cambios sin commmitear. Para hacerlo a la fuerza, ejecuta: $ git reset --hard [COMMIT] *Branch*: El borrado de una rama falla si esto causa que se pierdan cambios, para forzarlo escribe: $ git branch -D rama_muerta # en lugar de -d De forma similar, intentar sobreescribir una rama moviendo otra, falla si esto resultase en pérdida de datos. Para forzar el mover una rama, corre: $ git branch -M [ORIGEN] DESTINO # en lugar de -m A diferencia de checkout y reset, estos dos comandos evitan la destrucción de datos. Los cambios están aún guardados en el subdirectorio .git, y pueden obtenerse recuperando el hash apropiado de `.git/logs` (ver "Cazando Cabezas" arriba). Por defecto, serán guardados por al menos dos semanas. *Clean*: Algunos comandos de Git se rehúsan a proceder porque están preocupados de destruir archivos no monitoreados. Si tienes la certeza de que todos los archivos y directorios sin monitorear son prescindibles, se pueden borrar sin piedad con: $ git clean -f -d ¡La próxima vez, ese comando molesto va a funcionar! === Mejora Tu Imagen Pública === Los errores estúpidos abundan en la historia de muchos proyectos. El más preocupante son los archivos perdidos por el olvido de ejecutar *git add*. Por suerte nunca perdí datos cruciales por omisión accidental, dado que muy rara vez elimino directorios de trabajo originales. Lo normal es que note el error un par de commits mas adelante, por lo que el único daño es un poco de historia perdida y el tener que admitir la culpa. También me preocupo por no tenes espacios en blanco al final de las líneas. Aunque son inofensivos, procuro que nunca aparezcan en la historia pública. Además, si bien nunca me sucedió, me preocupo por no dejar conflictos de merge sin resolver. Usualmente los descubro al compilar el proyecto, pero hay algunos casos en los que se puede no notar. Es útil comprar un seguro contra la idiotez, usando un _hook_ para alertarme de estos problemas: $ cd .git/hooks $ cp pre-commit.sample pre-commit # En versiones mas viejas de Git: chmod +x pre-commit Ahora Git aborta un commit si se detectan espacios inútiles en blanco o conflictos de merge sin resolver. Para esta guía, eventualmente agregué lo siguiente al inicio del hook *pre-commit*, pare prevenirme de la desatención. if git ls-files -o | grep '\.txt$'; then echo FALLA! Archivos .txt sin monitorear. exit 1 fi Varias operaciones de git soportan hooks; ver *git help hooks*. Se pueden escribir hooks para quejarse de errores ortográficos en los mensajes de commit, agregar nuevos archivos, indentar párrafos, agregar una entrada en una página, reproducir un sonido, etc. Habíamos activado el hook *post-update* antes, cuando discutíamos como usar Git sobre HTTP. Los hooks se ejecutan cada vez que la rama HEAD sufre cambios. Este hook en particular actualiza algunos archivos que Git necesita para comunicación no nativa (como HTTP). gitmagic-20160304/es/history.txt0000644000175000017500000002510312666307504015652 0ustar sbadiasbadia== Lecciones de Historia == Una consecuencia de la naturaleza distribuída de git, es que la historia puede ser editada fácilmente. Pero si manipulas el pasado, ten cuidado: solo reescribe la parte de la historia que solamente tú posees. Así como las naciones discuten eternamente sobre quién cometió qué atrocidad, si otra persona tiene un clon cuya versión de la historia difiere de la tuya, vas a tener problemas para reconciliar ambos árboles cuando éstos interactúen. Por supuesto, si también controlas todos los demás árboles, puedes simplemente sobreescribirlos. Algunos desarrolladores están convencidos de que la historia debería ser inmutable, incluso con sus defectos. Otros sienten que los árboles deberían estar presentables antes de ser mostrados en público. Git satisface ambos puntos de vista. Al igual que el clonar, hacer branches y hacer merges, reescribir la historia es simplemente otro poder que Git te da. Está en tus manos usarlo con sabiduría. === Me corrijo === ¿Hiciste un commit, pero preferirías haber escrito un mensaje diferente? Entonces escribe: $ git commit --amend para cambiar el último mensaje. ¿Te olvidaste de agregar un archivo? Ejecuta *git add* para agregarlo, y luego corre el comando de arriba. ¿Quieres incluir algunas ediciones mas en ese último commit? Edita y luego escribe: $ git commit --amend -a === ... Y Algo Más === Supongamos que el problema anterior es diez veces peor. Luego de una larga sesión hiciste unos cuantos commits. Pero no estás conforme con la forma en que están organizados, y a algunos de los mensajes de esos commits les vendría bien una reescritura. Entonces escribe: $ git rebase -i HEAD~10 y los últimos 10 commits van a aparecer en tu $EDITOR favorito. Un fragmento de muestra: pick 5c6eb73 Added repo.or.cz link pick a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile Entonces: - Elimina commits borrando líneas. - Reordena commits reordenando líneas. - Reemplaza "pick" por "edit" para marcar un commit para arreglarlo. - Reemplaza "pick" por "squash" para unir un commit con el anterior. Si marcaste un commit para edición, entonces ejecuta: $ git commit --amend En caso contrario, corre: $ git rebase --continue Por lo tanto, es bueno hacer commits temprano y seguido: siempre se puede acomodar después usando rebase. === Los Cambios Locales Al Final === Estás trabajando en un proyecto activo. Haces algunos commits locales por un tiempo, y entonces sincronizas con el árbol oficial usando un merge. Este ciclo se repite unas cuantas veces antes de estar listo para hacer push hacia el árbol central. El problema es que ahora la historia en tu clon local de Git, es un entrevero de tus cambios y los cambios oficiales. Preferirías ver todos tus cambios en una sección contigua, luego de todos los cambios oficiales. Lo descrito arriba es un trabajo para *git rebase*. En muchos casos se puede usar el parámetro *--onto* y evitar la interacción. Ver *git help rebase* para ejemplos detallados de este asombroso comando. Se pueden partir commits. Incluso se pueden reordenar las branches de un árbol. === Reescribiendo la Historia === Ocasionalmente, se necesita algo equivalente a borrar gente de fotos oficiales, pero para control de código, para borrar cosas de la historia de manera Stalinesca. Por ejemplo, supongamos que queremos lanzar un proyecto, pero involucra un archivo que debería ser privado por alguna razón. Quizás dejé mi número de tarjeta de crédito en un archivo de texto y accidentalmente lo agregué al proyecto. Borrar el archivo es insuficiente, dado que se puede acceder a él en commits viejos. Debemos eliminar el archivo de todos los commits: $ git filter-branch --tree-filter 'rm archivo/secreto' HEAD Ver *git help filter-branch*, donde se discute este ejemplo y se da un método más rápido. En general, *filter-branch* permite alterar grandes secciones de la historia con un solo comando. Luego, el directorio +.git/refs/original+ describe el estado de las cosas antes de la operación. Revisa que el comando filter-branch hizo lo que querías, y luego borra este directorio si deseas ejecutar más comandos filter-branch. Por último, reemplaza los clones de tu proyecto con tu versión revisada si pretendes interactuar con ellos en un futuro. === Haciendo Historia === [[makinghistory]] ¿Quieres migrar un proyecto a Git? Si está siendo administrado con alguno de los sistemas más conocidos, hay grandes posibilidades de que alguien haya escrito un script para exportar la historia completa a Git. Si no lo hay, revisa *git fast-import*, que lee una entrada de texto en un formato específico para crear una historia de Git desde la nada. Típicamente un script que usa este comando se acomoda de apuro y se corre una sola vez, migrando el proyecto de un solo tiro. Como ejemplo, pega el texto a continuación en un archivo temporal, como ser `/tmp/history`: ---------------------------------- commit refs/heads/master committer Alice Thu, 01 Jan 1970 00:00:00 +0000 data < int main() { printf("Hola mundo!\n"); return 0; } EOT commit refs/heads/master committer Bob Tue, 14 Mar 2000 01:59:26 -0800 data < int main() { write(1, "Hola mundo!\n", 14); return 0; } EOT ---------------------------------- Luego crea un repositorio Git desde este archivo temporal escribiendo: $ mkdir project; cd project; git init $ git fast-import < /tmp/history Puedes hacer checkout de la última versión del proyecto con: $ git checkout master . El comando *git fast-export* convierte cualquier repositorio de git al formato de *git fast-import*, y puedes estudiar su salida para escribir exportadores, y también para transportar repositorios de git en un formato legible por humanos. De hecho estos comandos pueden enviar repositorios de archivos de texto sobre canales de solo texto. === ¿Dónde Nos Equivocamos? === Acabas de descubrir una prestación rota en tu programa, y estás seguro que hace unos pocos meses funcionaba. ¡Argh! ¿De donde salió este bug? Si solo hubieras ido testeando a medida que desarrollabas. Es demasiado tarde para eso ahora. De todos modos, dado que haz ido haciendo commits seguido, Git puede señalar la ubicación del problema. $ git bisect start $ git bisect bad SHA1_DE_LA_VERSION_MALA $ git bisect good SHA1_DE_LA_VERSION_BUENA Git hace checkout de un estado a mitad de camino. Prueba la funcionalidad, y si aún está rota: $ git bisect bad Si no lo está, reemplaza "bad" por "good". Git una vez más te transporta a un estado en mitad de camino de las versiones buena y mala, acortando las posibilidades. Luego de algunas iteraciones, esta búsqueda binaria va a llevarte al commit que causó el problema. Una vez que hayas terminado tu investigación, vuelve a tu estado original escribiendo: $ git bisect reset En lugar de testear cada cambio a mano, automatiza la búsqueda escribiendo: $ git bisect run COMANDO Git utiliza el valor de retorno del comando dado, típicamente un script hecho solo para eso, para decidir si un cambio es bueno o malo: el comando debería salir con código 0 si es bueno, 125 si el cambio se debería saltear, y cualquier cosa entre 1 y 127 si es malo. Un valor negativo aborta el bisect. Puedes hacer mucho más: la página de ayuda explica como visualizar bisects, examinar o reproducir el log de un bisect, y eliminar cambios inocentes conocidos para que la búsqueda sea más rápida. === ¿Quién Se Equivocó? === Como muchos otros sistemas de control de versiones, Git tiene un comando blame: $ git blame ARCHIVO que anota cada línea en el archivo dado mostrando quién fue el último en cambiarlo y cuando. A diferencia de muchos otros sistemas de control de versiones, esta operación trabaja desconectada, leyendo solo del disco local. === Experiencia Personal === En un sistema de control de versiones centralizado, la modificación de la historia es una operación dificultosa, y solo disponible para administradores. Clonar, hacer branches y merges, es imposible sin comunicación de red. Lo mismo para operaciones básicas como explorar la historia, o hacer commit de un cambio. En algunos sistemas, los usuarios requieren conectividad de red solo para ver sus propios cambios o abrir un archivo para edición. Los sistemas centralizados no permiten trabajar desconectado, y necesitan una infraestructura de red más cara, especialmente a medida que aumenta el número de desarrolladores. Lo más importante, todas las operaciones son más lentas de alguna forma, usualmente al punto donde los usuarios evitan comandos avanzados a menos que sean absolutamente necesarios. En casos extremos esto se da incluso para los comandos más básicos. Cuando los usuarios deben correr comandos lentos, la productividad sufre por culpa de un flujo de trabajo interrumpido. Yo experimenté estos fenómenos de primera mano. Git fue el primer sistema de control de versiones que usé. Me acostumbré rápidamente a él, dando por ciertas varias funcionalidades. Simplemente asumí que otros sistemas eran similares: elegir un sistema de control de versiones no debería ser diferente de elegir un editor de texto o navegador web. Cuando me vi obligado a usar un sistema centralizado me sorprendí. Una mala conexión a internet importa poco con Git, pero hace el desarrollo insoportable cuando se necesita que sea confiable como un disco local. Adicionalmente me encontré condicionado a evitar ciertos comandos por las latencias involucradas, lo que terminó evitando que pudiera seguir mi flujo de trabajo deseado. Cuando tenía que correr un comando lento, la interrupción de mi tren de pensamiento generaba una cantidad de daño desproporcionada. Mientras esperaba que se complete la comunicación con el servidor, hacía alguna otra cosa para pasar el tiempo, como revisar el e-mail o escribir documentación. A la hora de volver a la tarea original, el comando había terminado hace tiempo, y yo perdía más tiempo intentando recordar qué era lo que estaba haciendo. Los humanos no son buenos para el cambio de contexto. También ocurría un interesante efecto de "tragedia-de-los-comunes": anticipando la congestión de la red, la gente consume más ancho de banda que el necesario en varias operaciones, intentando anticipar futuras demoras. El esfuerzo combinado intensifica la congestión, alentando a las personas a consumir aún más ancho de banda la próxima vez para evitar demoras incluso más largas. gitmagic-20160304/es/intro.txt0000644000175000017500000001627412666307504015315 0ustar sbadiasbadia== Introducción == Voy a usar una analogía para explicar el control de versiones. Mira http://es.wikipedia.org/wiki/Control_de_versiones[el artículo de Wikipedia sobre control de versiones] para una explicación más cuerda. === Trabajar Es Jugar === He jugado juegos de PC casi toda mi vida. En cambio, empecé a usar sistemas de control de versiones siendo adulto. Sospecho que no soy el único, y comparar ambas cosas puede hacer que estos conceptos sean más fáciles de explicar y entender. Piensa en editar tu código o documento, o lo que sea, como si fuera jugar un juego. Una vez que progresaste mucho, te gustaría guardar. Para lograrlo, haces clic en el botón de "Guardar" en tu editor de confianza. Pero esto va a sobreescribir tu versión antigua. Es como esos viejos juegos que solo tenían un slot para guardar: se podía guardar, pero nunca podías volver a un estado anterior. Esto era una pena, porque tu versión anterior podía haber estado justo en una parte que era particularmente divertida, y podías querer volver a jugarla algún día. O peor aún, tu partida actual está en un estado donde es imposible ganar, y tienes que volver a empezar. === Control De Versiones === Cuando estás editando, puedes "Guardar Como..." un archivo diferente, o copiar el archivo a otro lugar antes de guardar si quieres probar versiones viejas. También puedes usar compresión para ahorrar espacio. Esta es una forma primitiva y muy trabajosa de control de versiones. Los videojuegos han mejorado esto hace ya tiempo, muchas veces permitiendo guardar en varios slots, fechados automáticamente. Hagamos que el problema sea un poco más complejo. Imagina que tienes un montón de archivos que van juntos, como el código fuente de un proyecto, o archivos para un sitio web. Ahora, si quieres mantener una vieja versión, debes archivar un directorio completo. Tener muchas versiones a mano es inconveniente y rápidamente se vuelve costoso. Con algunos juegos, una partida guardada en realidad consiste de un directorio lleno de archivos. Estos videojuegos ocultan este detalle del jugador y presentan una interfaz conveniente para administrar diferentes versiones de este directorio. Los sistemas de control de versiones no son diferentes. Todos tienen lindas interfaces para administrar un directorio de cosas. Puedes guardar el estado del directorio tantas veces como quieras, y tiempo después puedes cargar cualquiera de los estados guardados. A diferencia de la mayoría de los juegos, normalmente estos sistemas son inteligentes en cuanto la conservación del espacio. Por lo general, solo algunos pocos archivos cambian de versión a versión, y no es un gran cambio. Guardar las diferencias en lugar de nuevas copias ahorra espacio. === Control Distribuído === Ahora imagina un juego muy difícil. Tan difícil para terminar, que muchos jugadores experimentados alrededor del mundo deciden agruparse e intercambiar sus juegos guardados para intentar terminarlo. Los "Speedruns" son ejemplos de la vida real: los jugadores se especializan en diferentes niveles del mismo juego y colaboran para lograr resultados sorprendentes. ¿Cómo armarías un sistema para que puedan descargar las partidas de los otros de manera simple? ¿Y para que suban las nuevas? Antes, cada proyecto usaba un control de versiones centralizado. Un servidor en algún lado contenía todos los juegos salvados. Nadie más los tenía. Cada jugador tenía a lo sumo un par de juegos guardados en su máquina. Cuando un jugador quería progresar, obtenía la última versión del servidor principal, jugaba un rato, guardaba y volvía a subir al servidor para que todos los demás pudieran usarlo. ¿Qué pasa si un jugador quería obtener un juego anterior por algún motivo? Tal vez el juego actual está en un estado donde es imposible ganar, porque alguien olvidó obtener un objeto antes de pasar el nivel tres, por que se quiere obtener el último juego guardado donde todavía es posible completarlo. O tal vez quieren comparar dos estados antiguos, para ver cuánto trabajo hizo un jugador en particular. Puede haber varias razones para querer ver una revisión antigua, pero el resultado es siempre el mismo. Tienen que pedirle esa vieja partida al servidor central. Mientras mas juegos guardados se quieran, más se necesita esa comunicación. La nueva generación de sistemas de control de versiones, de la cual Git es miembro, se conoce como sistemas distribuídos, y se puede pensar en ella como una generalización de sistemas centralizados. Cuando los jugadores descargan del servidor central, obtienen todos los juegos guardados, no solo el último. Es como si tuvieran un mirror del servidor central. Esta operación inicial de clonado, puede ser cara, especialmente si el historial es largo, pero a la larga termina siendo mejor. Un beneficio inmediato es que cuando se quiere una versión vieja por el motivo que sea, la comunicación con el servidor es innecesaria. ==== Una Tonta Superstición ==== Una creencia popular errónea es que los sistemas distribuídos son poco apropiados para proyectos que requieren un repositorio central oficial. Nada podría estar más lejos de la verdad. Fotografiar a alguien no hace que su alma sea robada, clonar el repositorio central no disminuye su importancia. Una buena aproximación inicial, es que cualquier cosa que se puede hacer con un sistema de control de versiones centralizado, se puede hacer mejor con un sistema de versiones distribuído que esté bien diseñado. Los recursos de red son simplemente más costosos que los recursos locales. Aunque luego veremos que hay algunas desventajas para un sistema distribuído, hay menos probabilidad de hacer comparaciones erróneas al tener esto en cuenta. Un proyecto pequeño, puede necesitar solo una fracción de las características que un sistema así ofrece. Pero, ¿usarías números romanos si solo necesitas usar números pequeños?. Además, tu proyecto puede crecer más allá de tus expectativas originales. Usar Git desde el comienzo, es como llevar una navaja suiza, aunque solo pretendas usarla para abrir botellas. El día que necesites desesperadamente un destornillador, vas a agradecer el tener más que un simple destapador. === Conflictos al fusionar === Para este tema, habría que estirar demasiado nuestra analogía con un videojuego. En lugar de eso, esta vez consideremos editar un documento. Supongamos que Alice inserta una línea al comienzo de un archivo, y Bob agrega una línea al final de su copia. Ambos suben sus cambios. La mayoría de los sistemas automáticamente van a deducir un accionar razonable: aceptar y hacer merge (Nota del Traductor: fusionar en inglés) de los cambios, para que tanto la edición de Alice como la de Bob sean aplicadas. Ahora supongamos que Alice y Bob han hecho ediciones distintas sobre la misma línea. Entonces es imposible resolver el conflicto sin intervención humana.Se le informa a la segunda persona en hacer upload que hay un conflicto de merge, y ellos deben elegir entre ambas ediciones, o cambiar la línea por completo. Pueden surgir situaciones más complejas. Los sistemas de control de versiones manejan automáticamente los casos simples, y dejan los más complejos para los humanos. Usualmente este comportamiento es configurable. gitmagic-20160304/es/clone.txt0000644000175000017500000001265012666307504015254 0ustar sbadiasbadia== Clonando == En sistemas de control de versiones antiguos, checkout es la operación standard para obtener archivos. Obtienes un conjunto de archivos en el estado guardado que solicitaste. En Git, y otros sistemas de control de versiones distribuídos, clonar es la operación standard. Para obtener archivos se crea un clon de un repositorio entero. En otras palabras, prácticamente se crea una copia idéntica del servidor central. Todo lo que se pueda hacer en el repositorio principal, también podrás hacerlo. === Sincronizar Computadoras === Este es el motivo por el que usé Git por primera vez. Puedo tolerar hacer tarballs o usar *rsync* para backups y sincronización básica. Pero algunas veces edito en mi laptop, otras veces en mi desktop, y ambas pueden no haberse comunicado en el medio. Inicializa un repositorio de Git y haz commit de tus archivos en una máquina, luego en la otra: $ git clone otra.computadora:/ruta/a/archivos para crear una segunda copia de los archivos y el repositorio Git. De ahora en más, $ git commit -a $ git pull otra.computadora:/ruta/a/archivos HEAD va a traer (pull) el estado de los archivos desde la otra máquina hacia la que estás trabajando. Si haz hecho cambios que generen conflictos en un archivo, Git te va a avisar y deberías hacer commit luego de resolverlos. === Control Clásico de Fuentes === Inicializa un repositorio de Git para tus archivos: $ git init $ git add . $ git commit -m "Commit Inicial" En el servidor central, inicializa un repositorio vacío de Git con algún nombre, y abre el Git daemon si es necesario: $ GIT_DIR=proj.git git init $ git daemon --detach # podría ya estar corriendo Algunos servidores públicos, como http://repo.or.cz[repo.or.cz], tienen un método diferente para configurar el repositorio inicialmente vacío de Git, como llenar un formulario en una página. Empuja (push) tu proyecto hacia el servidor central con: $ git push git://servidor.central/ruta/al/proyecto.git HEAD Ya estamos listos. Para copiarse los fuentes, un desarrollador escribe: $ git clone git://servidor.central/ruta/al/proyecto.git Luego de hacer cambios, el código en envía al servidor central con: $ git commit -a $ git push Si hubo actualizaciones en el servidor principal, la última versión debe ser traída antes de enviar lo nuevo. Para sincronizar con la última versión: $ git commit -a $ git pull === Bifurcando (fork) un proyecto === ¿Harto de la forma en la que se maneja un proyecto?¿Crees que podrías hacerlo mejor? Entonces en tu servidor: $ git clone git://servidor.principal/ruta/a/archivos Luego avísale a todos de tu fork del proyecto en tu servidor. Luego, en cualquier momento, puedes unir (merge) los cambios del proyecto original con: $ git pull === Respaldos Definitivos === ¿Quieres varios respaldos redundantes a prueba de manipulación y geográficamente diversos? Si tu proyecto tiene varios desarrolladores, ¡no hagas nada! Cada clon de tu código es un backup efectivo. No sólo del estado actual, sino que también de la historia completa de tu proyecto. Gracias al hashing criptográfico, si hay corrupción en cualquiera de los clones, va a ser detectado tan pronto como intente comunicarse con otros. Si tu proyecto no es tan popular, busca tantos servidores como puedas para hospedar tus clones. El verdadero paranoico debería siempre escribir el último hash SHA1 de 20-bytes de su HEAD en algún lugar seguro. Tiene que ser seguro, no privado. Por ejemplo, publicarlo en un diario funcionaría bien, porque es difícil para un atacante el alterar cada copia de un diario. === Multitask A La Velocidad De La Luz === Digamos que quieres trabajar en varias prestaciones a la vez. Haz commit de tu proyecto y ejecuta: $ git clone . /un/nuevo/directorio Gracias a los http://es.wikipedia.org/wiki/Enlace_duro[enlaces duros], los clones locales requieren menos tiempo y espacio que un backup plano. Ahora podrás trabajar en dos prestaciones independientes de manera simultánea. Por ejemplo, puedes editar un clon mientras el otro está compilando. En cualquier momento, puedes hacer commit y pull de los cambios desde el otro clon. $ git pull /el/otro/clon HEAD === Control Guerrillero De Versiones === ¿Estás trabajando en un proyecto que usa algún otro sistema de control de versiones y extrañas mucho a Git? Entonces inicializa un repositorio de Git en tu directorio de trabajo. $ git init $ git add . $ git commit -m "Commit Inicial" y luego clónalo: $ git clone . /un/nuevo/directorio Ahora debes trabajar en el nuevo directorio, usando Git como te sea más cómodo. Cada tanto, querrás sincronizar con los demás, en ese caso, ve al directorio original, sincroniza usando el otro sistema de control de versiones y escribe: $ git add . $ git commit -m "Sincronizo con los demás" Luego ve al nuevo directorio y escribe: $ git commit -a -m "Descripción de mis cambios" $ git pull El procedimiento para pasarle tus cambios a los demás depende de cuál es tu otro sistema de control de versiones. El nuevo directorio contiene los archivos con tus cambios. Ejecuta los comandos que sean necesarios para subirlos al repositorio central del otro sistema de control de versiones. El comando *git svn* automatiza lo anterior para repositorios de Subversion, y también puede ser usado para http://google-opensource.blogspot.com/ncr/2008/05/export-git-project-to-google-code.html[exportar un proyecto de Git a un repositorio de Subversion]. gitmagic-20160304/custom-html.xsl0000644000175000017500000000251112666307504016003 0ustar sbadiasbadia ul gitmagic-20160304/fr/0000755000175000017500000000000012666307504013407 5ustar sbadiasbadiagitmagic-20160304/fr/drawbacks.txt0000644000175000017500000002104612666307504016114 0ustar sbadiasbadia// -*- mode: doc; mode: flyspell; coding: utf-8; fill-column: 79; -*- == Appendix A: Les lacunes de Git == Git présente quelques problèmes que j'ai soigneusement cachés. Certains peuvent être résolus par des scripts et des hooks, d'autres nécessitent une réorganisation ou une redéfinition du projet et pour les quelques rares ennuis restants, il vous suffit d'attendre. Ou mieux encore, de donner un coup de main. === Les faiblesses de SHA1 === Avec le temps, les spécialistes de cryptographie découvrent de plus en plus de faiblesses de SHA1. À ce jour, la découverte de collisions d'empreintes semble à la portée d'organisations bien dotées. Et d'ici quelques années, peut-être que même un simple PC aura assez de puissance de calcul pour corrompre de manière indétectable un dépôt Git. Heureusement Git aura migré vers une fonction de calcul d'empreintes de meilleure qualité avant que de futures recherches détruisent SHA1. === Microsoft Windows === Git sur Microsoft Windows peut être jugé encombrant : - http://cygwin.com/[Cygwin] est un environnement de type Linux dans Windows proposant http://cygwin.com/packages/git/[un portage de Git]. - http://code.google.com/p/msysgit/[Git on MSys] est un autre choix nécessitant beaucoup moins de place. Néanmoins quelques commandes doivent encore être améliorées. === Des fichiers sans relation === Si votre projet est très gros et contient de nombreux fichiers sans relation entre eux et changeant constamment, Git peut être plus défavorisé que d'autres systèmes puisque les fichiers pris séparément ne sont pas pistés. Git piste les changement de l'ensemble du projet, ce qui est habituellement bénéfique. Une solution consiste à découper votre projet en plusieurs parties, chacune réunissant des fichiers en relation entre eux. Utilisez *git submodule* si vous souhaitez conserver tout cela dans un seul dossier. === Qui modifie quoi ? === Certains systèmes de gestion de versions vous oblige à marquer explicitement un fichier avant de pouvoir le modifier. Bien que particulièrement ennuyeux puisque pouvant impliquer une communication avec un serveur central, cela présente deux avantages : 1. Les diffs sont plus rapides puisque seuls les fichiers marqués doivent être examinés. 2. Quelqu'un peut savoir qui travaille sur un fichier en demandant au serveur central qui l'a marqué pour modification. Avec quelques scripts appropriés, vous pouvez obtenir la même chose avec Git. Cela nécessite la coopération du développeur qui doit exécuter un script particulier avant toute modification d'un fichier. === L'historique d'un fichier === Puisque Git enregistre les modifications de manière globale au projet, la reconstruction de l'historique d'un seul fichier demande plus de travail qu'avec un système de gestion de versions qui traque les fichiers individuellement. Ce surplus est généralement négligeable et en vaut la peine puisque cela permet aux autres opérations d'être incroyablement efficaces. Par exemple, `git checkout` est plus rapide que `cp -a` et un delta de versions globale au projet se compresse mieux qu'une collection de delta fichier par fichier. === Le clone initial === La création d'un clone est plus coûteuse que l'extraction de code des autres systèmes quand il y a un historique conséquent. Ce coût initial s'avère payant dans le temps puisque la plupart des opérations futures s'effectueront rapidement et hors-ligne. En revanche, dans certains situations, il est préférable de créer un clone superficiel grâce à l'option `--depth` (qui limite la profondeur de l'historique). C'est plus rapide mais le clone ainsi créé offre des fonctionnalités réduites. === Les projets versatiles === Git a été conçu pour être rapide au regard de la taille des changements. Les humains font de petits changement de version en version. Une correction de bug en une ligne ici, une nouvelle fonctionnalité là, un commentaire amendé ailleurs... Mais si vos fichiers changent radicalement à chaque révision alors, à chaque commit, votre historique grossit d'un poids équivalent à celui de votre projet. Il n'y a rien qu'un système de gestion de versions puisse faire pour éviter cela, mais les utilisateurs de Git en souffrent plus puisque chaque clone contient habituellement l'historique complet. Il faut rechercher les raisons de ces changements radicaux. Peut-être faut-il changer les formats des fichiers. Des modifications mineures ne devraient modifier que très peu de chose dans très peu de fichiers. Peut-être qu'une base données ou une solution d'archivage est-elle plus adaptée comme solution qu'un système de gestion de versions. À titre d'exemple, un système de gestion de versions n'est certainement pas bien taillé pour gérer des photos prises périodiquement par une webcam. Si les fichiers doivent absolument se transformer constamment et s'il faut absolument les gérer par version, une possibilité peut être une utilisation centralisée d'un dépôt Git. Chacun ne crée qu'un clone superficiel ne contenant qu'un historique récent voire inexistant du projet. Évidemment de nombreux outils Git ne seront plus utilisables et les corrections devront être fournies sous forme de patches. C'est sans doute acceptable sans en savoir plus sur les raisons réelles de la conservation de l'historique de nombreux fichiers instables. Un autre exemple serait un projet dépendant d'un firmware qui prend la forme d'un énorme fichier binaire. L'historique de ce firmware n'intéresse pas les utilisateur et les mises à jour se compressent difficilement et donc les révisions de ce firmware vont faire grossir inutilement le dépôt. Dans ce cas, le code source devrait être stocké dans le dépôt Git et les fichiers binaires conservés séparément. Pour rendre la vie meilleure, on peut distribuer un script qui utilisera un clone Git pour le code et rsync ou un clone Git superficiel pour le firmware. === Compteur global === Certains systèmes de gestion de versions centralisés gère un entier positif qui augmente à chaque commit accepté. Git fait référence à un changement par son empreinte ce qui est mieux pour de nombreuses raisons. Mais certains aiment voir ce compteur. Par chance, il est très facile d'écrire un script qui se déclenchera à chaque mise à jour du dépôt Git central et incrémentera un compteur, peut-être dans un tag, qu'il associera à l'empreinte du dernier commit. Chaque clone peut gérer un tel compteur mais c'est probablement sans intérêt puisque seul le compteur du dépôt central compte. === Les dossiers vides === Les sous-dossiers vides ne peuvent pas être suivis. Placez-y des fichiers sans intérêt pour remédier à ce problème. Cette limitation n'est pas une fatalité due à la conception de Git mais un choix de l'implémentation actuelle. Avec un peu de chance, si de nombreux utilisateurs le demandent, cette fonctionnalité pourrait être ajoutée. === Le premier commit === Un informaticien typique compte à partir de 0 plutôt que de 1. Malheureusement, concernant les commits, Git n'adhère pas à cette convention. Plusieurs commandes ne fonctionnent pas avant le tout premier commit. De plus, certains cas limites doivent être gérés spécifiquement : par exemple, un rebasage vers une branche avec un commit initial différent. Git bénéficierait à définir le commit zéro : dès la création d'un dépôt, HEAD serait défini comme la chaîne contenant 20 octets nuls. Ce commit spécial représenterait un arbre vide, sans parent, qui serait présent dans tous les dépôts Git. Ainsi l'appel à git log, par exemple, pourrait indiquer à l'utilisateur qu'aucun commit n'a été fait au lieu de se terminer par une erreur fatale. Il en serait de même pour les autres outils. Le commit initial serait un descendant implicite de ce commit zéro. Mais ce n'est pas le cas et donc certains problèmes peuvent se poser. Si plusieurs branches avec des commits initiaux différents sont fusionnées alors le rebasage du résultat requiert de nombreuses interventions manuelles. === Bizarreries de l'interface === Étant donné deux commits A et B, le sens des expressions "A..B" et "A...B" diffère selon que la commande attend deux extrémités ou un intervalle. Voir *git help diff* et *git help rev-parse*. // LocalWords: Appendix Git hooks SHA PC indétectable Microsoft Windows // LocalWords: Cygwin à-la-Linux MSys git submodule diffs checkout cp depth // LocalWords: bug faut-il webcam patches firmware rsync tag Placez-y log // LocalWords: l'implémentation commits rebasage HEAD help diff rev-parse gitmagic-20160304/fr/branch.txt0000644000175000017500000003415612666307504015416 0ustar sbadiasbadia// -*- mode: doc; mode: flyspell; coding: utf-8; fill-column: 79; -*- == La sorcellerie des branches == Des branchements et des fusions quasi-instantanés sont les fonctionnalités les plus puissantes qui font de Git un vrai tueur. *Problème* : des facteurs externes amènent nécessairement à des changements de contexte. Un gros bug se manifeste sans avertissement dans la version déployée. La date limite pour une fonctionnalité particulière est avancée. Un développeur qui vous aidait pour une partie clé du projet n'est plus disponible. Bref, en tous cas, vous devez brusquement arrêter la tâche en cours pour vous focaliser sur une tâche tout autre. Interrompre votre réflexion peut être nuisible à votre productivité et le changement de contexte amène encore plus de perte. Avec un système de gestion de versions centralisé, il faudrait télécharger une nouvelle copie de travail depuis le serveur central. Un système de gestion de versions décentralisé est bien meilleur puisqu'il peut cloner localement la version voulue. Mais un clone implique encore la copie de tout le dossier de travail ainsi que de l'historique complet jusqu'au point voulu. Même si Git réduit ce coût grâce aux fichiers partagés et au liens matériels, les fichiers du projet doivent tout de même être entièrement recréés dans le nouveau dossier de travail. *Solution* : dans ce genre de situations, Git offre un outil bien meilleur puisque plus rapide et moins consommateur d'espace disque : les branches. Grâce à un mot magique, les fichiers de votre dossier se transforment d'une version à une autre. Cette transformation peut être bien plus qu'un simple voyage dans l'historique. Vos fichiers peuvent se transformer de la dernière version stable vers une version expérimentale, vers la version courante de développement, vers la version d'un collègue, etc. === La touche du chef === N'avez-vous jamais joué à l'un de ces jeux qui, à l'appui d'une touche particulière (la ``touche du chef''), affiche instantanément une feuille de calcul ? Ceci vous permet de cacher votre écran de jeu dès que le chef arrive. Dans un dossier vide : $ echo "Je suis plus intelligent que mon chef." > myfile.txt $ git init $ git add . $ git commit -m "Commit initial" Vous venez de créer un dépôt Git qui gère un fichier contenant un message. Maintenant tapez : $ git checkout -b chef # rien ne semble avoir changé $ echo "Mon chef est plus intelligent que moi." > myfile.txt $ git commit -a -m "Un autre commit" Tout se présente comme si vous aviez réécrit votre fichier et intégrer (commit) ce changement. Mais ce n'est qu'une illusion. Tapez : $ git checkout master # bascule vers la version originale du fichier et ça y est ! Le fichier texte est restauré. Et si le chef repasse pour regarder votre dossier, tapez : $ git checkout chef # bascule vers la version visible par le chef Vous pouvez basculer entre ces deux versions autant de fois que voulu, et intégrer (commit) vos changements à chacune d'elles indépendamment. === Travail temporaire === [[branch]] Supposons que vous travailliez sur une fonctionnalité et que, pour une raison quelconque, vous ayez besoin de revenir trois versions en arrière afin d'ajouter temporairement quelques instructions d'affichage pour voir comment quelque chose fonctionne. Faites : $ git commit -a $ git checkout HEAD~3 Maintenant vous pouvez ajouter votre code temporaire là où vous le souhaitez. Vous pouvez même intégrer (commit) vos changements. Lorsque vous avez terminé, tapez : $ git checkout master pour retourner à votre travail d'origine. Notez que tous les changement non intégrés sont définitivement perdus (NdT : les changements intégrés via commit sont conservés quelques jours et sont accessibles en connaissant leur empreinte SHA1). Que faire si vous voulez nommer ces changements temporaires ? Rien de plus simple : $ git checkout -b temporaire et faites un commit avant de rebasculer vers la branche master. Lorsque vous souhaitez revenir à vos changements temporaires, tapez simplement : $ git checkout temporaire Nous aborderons la commande _checkout_ plus en détail lorsque nous parlerons du chargement d'anciens états. Mais nous pouvons tout de même en dire quelques mots : les fichiers sont bien amenés dans l'état demandé mais en quittant la branche master. À ce moment, tout commit poussera nos fichiers sur une route différente, qui pourra être nommée plus tard. En d'autres termes, après un checkout vers un état ancien, Git nous place automatiquement dans une nouvelle branche anonyme qui pourra être nommée et enregistrée grâce à *git checkout -b*. === Corrections rapides === Vous travaillez sur une tâche particulière et on vous demande de tout laisser tomber pour corriger un nouveau bug découvert dans la version `1b6d...` : $ git commit -a $ git checkout -b correction 1b6d Puis quand vous avez corrigé le bug, saisissez : $ git commit -a -m "Bug corrigé" $ git checkout master pour vous ramener à votre tâche originale. Vous pouvez même fusionner ('merge') avec la correction de bug toute fraîche : $ git merge correction === Fusionner === Dans certains systèmes de gestion de versions, la création de branches est facile mais les fusionner est difficile. Avec Git, la fusion est si simple que vous n'y prêterez plus attention. En fait, nous avons déjà rencontré la fusion. La commande *pull* ramène ('fetch') une série de versions puis les fusionne ('merge') dans votre branche courante. Si vous n'avez effectué aucun changement local alors la fusion est un simple bon en avant (un _fast forward_), un cas dégénéré qui s'apparente au rapatriement de la dernière version dans un système de gestion de versions centralisé. Si vous avez effectué des changements locaux, Git les fusionnera automatiquement et préviendra s'il y a des conflits. Habituellement, une version à une seule 'version parente', qu'on appelle la version précédente. Une fusion de branches entre elles produit une version avec plusieurs parents. Ce qui pose la question suivante : à quelle version se réfère `HEAD~10` ? Puisqu'une version peut avoir plusieurs parents, par quel parent remonterons-nous ? Il s'avère que cette notation choisit toujours le premier parent. C'est souhaitable puisque la branche courante devient le premier parent lors d'une fusion. Nous nous intéressons plus fréquemment aux changements que nous avons faits dans la branche courante qu'à ceux fusionnés depuis d'autres branches. Vous pouvez choisir un parent spécifique grâce à l'accent circonflexe. Voici, par exemple, comment voir le log depuis le deuxième parent : $ git log HEAD^2 Vous pouvez omettre le numéro pour le premier parent. Voici, par exemple, comment voir les différences avec le premier parent ; $ git diff HEAD^ Vous pouvez combiner cette notation avec les autres. Par exemple : $ git checkout 1b6d^^2~10 -b ancien démarre la nouvelle branche ``ancien'' dans l'état correspondant à 10 versions en arrière du deuxième parent du premier parent de la version 1b6d. === Workflow sans interruption === La plupart du temps dans un projet de réalisation matérielle, la seconde étape du plan ne peut commencer que lorsque la première étape est terminée. Une voiture en réparation reste bloquée au garage jusqu'à la livraison d'une pièce. Le montage d'un prototype est suspendu en attendant la fabrication d'une puce. Les projets logiciels peuvent être similaires. La deuxième partie d'une nouvelle fonctionnalité doit attendre que la première partie soit sortie et testée. Certains projets exigent une validation de votre code avant son acceptation, vous êtes donc obligé d'attendre que la première partie soit validée avant de commencer la seconde. Grâce aux branches et aux fusions faciles, vous pouvez contourner les règles et travailler sur la partie 2 avant que la partie 1 soit officiellement prête. Supposons que vous ayez terminé la version correspondant à la partie 1 et que vous l'ayez envoyée pour validation. Supposons aussi que vous soyez dans la branche `master`. Alors, branchez-vous : $ git checkout -b part2 Ensuite, travaillez sur la partie 2 et intégrez (via `commit`) vos changements autant que nécessaire. L'erreur étant humaine, vous voudrez parfois revenir en arrière pour effectuer des corrections dans la partie 1. Évidemment, si vous êtes chanceux ou très bon, vous pouvez sauter ce passage. $ git checkout master # Retour à la partie 1 $ correction_des_bugs $ git commit -a # Intégration de la correction $ git checkout part2 # Retour à la partie 2 $ git merge master # Fusion de la correction. Finalement, la partie 1 est validée. $ git checkout master # Retour à la partie 1 $ diffusion des fichiers # Diffusion au reste du monde ! $ git merge part2 # Fusion de la partie 2 $ git branch -d part2 # Suppression de la branche 'part2'. À cet instant vous êtes à nouveau dans la branche `master` avec la partie 2 dans votre dossier de travail. Il est facile d'étendre cette astuce à de nombreuses branches. Il est aussi facile de créer une branche rétroactivement : imaginons qu'après 7 commits, vous vous rendiez compte que vous auriez dû créer une branche. Tapez alors : $ git branch -m master part2 # Renommer la branche "master" en "part2". $ git branch master HEAD~7 # Recréer une branche "master" 7 commits en arrière. La branche `master` contient alors uniquement la partie 1 et la branche `part2` contient le reste ; nous avons créé `master` sans basculer vers elle car nous souhaitons continuer à travailler sur `part2`. Ce n'est pas très courant. Jusqu'à présent nous avions toujours basculé vers une branche dès sa création, comme dans : $ git checkout HEAD~7 -b master # Créer une branche et basculer vers elle. === Réorganiser le foutoir === Peut-être aimez-vous travailler sur tous les aspects d'un projet dans la même branche. Vous souhaitez que votre travail en cours ne soit accessible qu'à vous-même et donc que les autres ne puissent voir vos versions que lorsqu'elles sont proprement organisées. Commencez par créer deux branches : $ git branch propre # Créer une branche pour les versions propres $ git checkout -b foutoir # Créer et basculer vers une branche pour le foutoir Ensuite, faites tout ce que vous voulez : corriger des bugs, ajouter des fonctionnalités, ajouter du code temporaire et faites-en des versions autant que voulu. Puis : $ git checkout propre $ git cherry-pick foutoir^^ applique les modifications de la version grand-mère de la version courante du ``foutoir'' à la branche ``propre''. Avec les cherry-picks appropriés vous pouvez construire une branche qui ne contient que le code permanent et où toutes les modifications qui marchent ensemble sont regroupées. === Gestion des branches === Pour lister toutes les branches, tapez : $ git branch Par défaut, vous commencez sur la branche nommée ``master''. Certains préconisent de laisser la branche ``master'' telle quelle et de créer de nouvelles branches pour vos propres modifications. Les options *-d* et *-m* vous permettent de supprimer et renommer les branches. Voir *git help branch*. La branche ``master'' est une convention utile. Les autres supposent que votre dépôt possède une telle branche et qu'elle contient la version officielle de votre projet. Bien qu'il soit possible de renommer ou d'effacer cette branche ``master'', il peut-être utile de respecter les traditions. === Les branches temporaires === Après un certain temps d'utilisation, vous vous apercevrez que vous créez fréquemment des branches éphémères toujours pour les mêmes raisons : elles vous servent juste à sauvegarder l'état courant, vous permettant ainsi de revenir momentanément à état précédent pour corriger un bug. C'est exactement comme si vous zappiez entre deux chaînes de télévision. Mais au lieu de presser deux boutons, il vous faut créer, basculer, fusionner et supprimer des branches temporaires. Par chance, Git propose un raccourci qui est aussi pratique que la télécommande de votre télévision : $ git stash Cela mémorise l'état courant dans un emplacement temporaire (un 'stash') et restaure l'état précédent. Votre dossier courant apparaît alors exactement comme il était avant que vous ne commenciez à faire des modifications et vous pouvez corriger des bugs, aller rechercher (pull) une modification de dépôt central ou toute autre chose. Lorsque vous souhaitez revenir à l'état mémorisé dans votre 'stash', tapez : $ git stash apply # Peut-être faudra-t-il résoudre quelques conflits. Vous pouvez avoir plusieurs 'stash' et les manipuler de différents manières. Voir *git help stash*. Comme vous l'aurez deviné, pour faire ces tours de magie, dans les coulisses Git gère des branches. === Travailler comme il vous chante === Vous vous demandez sans doute si l'usage des branches en vaut la peine. Après tout, des clones sont tout aussi rapides et vous pouvez basculer de l'un à l'autre par un simple *cd* au lieu de commandes Git ésotériques. Considérez les navigateurs Web. Pourquoi proposer plusieurs onglets ainsi que plusieurs fenêtres ? Parce proposer les deux permet de s'adapter à une large gamme d'utilisations. Certains préfèrent n'avoir qu'une seule fenêtre avec plein d'onglets. D'autres font tout le contraire : plein de fenêtres avec un seul onglet. D'autres encore mélangent un peu des deux. Les branches ressemblent à des onglets de votre dossier de travail et les clones ressemblent aux différents fenêtres de votre navigateur. Ces opérations sont toutes rapides et locales. Alors expérimentez pour trouver la combinaison qui vous convient. Git vous laisse travailler exactement comme vous le souhaitez. // LocalWords: doc visual-line quasi-instantanés Git bug télécharger echo git // LocalWords: myfile.txt init add checkout master branch HEAD NdT SHA fetch // LocalWords: rebasculer commits merge fast forward bugs remonterons-nous log // LocalWords: diff Workflow branchez-vous aimez-vous faites-en cherry-pick cd // LocalWords: cherry-picks help zappiez stash apply faudra-t-il Web gitmagic-20160304/fr/secrets.txt0000644000175000017500000003606612666307504015633 0ustar sbadiasbadia// -*- mode: doc; mode: flyspell; coding: utf-8; fill-column: 79; -*- == Les secrets révélés == Nous allons jeter un œil sous le capot pour comprendre comment Git réalise ses miracles. Je passerai sous silence la plupart des détails. Pour des explications plus détaillées, référez-vous au http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[manuel utilisateur]. === L'invisibilité === Comment fait Git pour être si discret ? Mis à part lorsque vous faites des commits et des fusions, vous pouvez travailler comme si la gestion de versions n'existait pas. Et c'est lorsque vous en avez besoin que vous êtes content de voir que Git veillait sur vous tout le temps. D'autres systèmes de gestion de versions vous mettent constamment aux prises avec de la paperasserie et de la bureaucratie. Les fichiers sont en lecture seule jusqu'à l'obtention depuis un serveur central du droit d'édition de tel ou tel fichier. Les commandes les plus basiques voient leurs performances s'écrouler au fur et à mesure que le nombre d'utilisateurs augmente. Le travail s'arrête dès lors que le réseau ou le serveur central est en panne. À l'inverse, Git conserve tout l'historique de votre projet dans le sous-dossier `.git` de votre dossier de travail. C'est votre propre copie de l'historique et vous pouvez donc rester déconnecté tant que vous ne voulez pas communiquer avec les autres. Vous conservez un contrôle total sur le sort de vos fichiers puisque Git peut aisément les recréer à tout moment à partir de l'un des états enregistrés dans `.git`. === L'intégrité === La plupart des gens associent la cryptographie à la conservation du secret des informations mais l'un de ses buts tout aussi important est de conserver l'intégrité de ces informations. Un usage approprié des fonctions de hachage cryptographiques (celles qui calculent l'empreinte d'un document) permet d'empêcher la corruption accidentelle ou malicieuse des données. Une empreinte SHA1 peut être vue comme un nombre de 160 bits identifiant de manière unique n'importe quelle suite d'octets que vous rencontrerez dans votre vie. On peut même aller plus loin : c'est vrai pour toutes les suites d'octets que les humains utiliseront sur plusieurs générations. Comme une empreinte SHA1 est elle-même une suite d'octets, nous pouvons calculer l'empreinte d'une suite de caractères contenant d'autres empreintes. Cette simple observation est étonnamment utile (cherchez par exemple 'hash chain'). Nous verrons plus tard comment Git utilise cela pour garantir efficacement l'intégrité des données. En bref, Git conserve vos données dans le sous-dossier `.git/objects` mais à la place des noms de fichiers normaux, vous n'y trouverez que des ID. En utilisant ces ID comme noms de fichiers et grâce à quelques astucieux fichiers de verrouillage et d'horodatage, Git transforme un simple système de fichiers en une base de données efficace et robuste. === L'intelligence === Comment fait Git pour savoir que vous avez renommé un fichier même si vous ne lui avez pas dit explicitement ? Bien sûr, vous pouvez utiliser *git mv* mais c'est exactement la même chose que de faire *git rm* suivi par *git add*. Git a des heuristiques pour débusquer les changements de noms et les copies entre les versions successives. En fait, il peut même détecter les bouts de code qui ont été déplacés ou copiés d'un fichier à un autre ! Bien que ne couvrant pas tous les cas, cela marche déjà très bien et cette fonctionnalité est encore en cours d'amélioration. Si cela échoue pour vous, essayez les options activant des méthodes de détection de copie plus coûteuses et envisager de faire une mise à jour. === L'indexation === Pour chaque fichier suivi, Git mémorise des informations, telles que sa taille et ses dates de création et de dernières modifications, dans un fichier appelé 'index'. Pour déterminer si un fichier a changé, Git compare son état courant avec ce qu'il a mémorisé dans l'index. Si cela correspond alors Git n'a pas besoin de relire le fichier. Puisque les appels à 'stat' sont considérablement plus rapides que la lecture des fichiers, si vous n'avez modifié que quelques fichiers, Git peut déterminer son état en très peu de temps. Nous avons dit plus tôt que l'index était une aire d'assemblage. Comment se peut-il qu'un simple fichier contant quelques informations sur les fichiers soit une aire d'assemblage ? Parce que la commande add ajoute les fichiers à la base de données de Git et met à jour l'index avec leurs informations alors que la commande commit, sans option, crée une nouvelle version basée uniquement sur cet index et les fichiers déjà inclus dans la base de données. === Les origines de Git === Ce http://lkml.org/lkml/2005/4/6/121[message de la Mailing List du noyau Linux] décrit l'enchaînement des évènements ayant mené à Git. L'ensemble de l'enfilade est un site archéologique fascinant pour les historiens de Git. === La base d'objets === Chacune des versions de vos données est conservée dans la base d'objets ('object database') qui réside dans le sous-dossier `.git/objects` ; le reste du contenu du dossier `.git` représente moins de données : l'index, le nom des branches, les tags, les options de configuration, les logs, l'emplacement actuel de HEAD, et ainsi de suite. La base d'objets est simple mais élégante et constitue la source de la puissance de Git. Chaque fichier dans `.git/objects` est un objet. Il y a trois sortes d'objets qui nous concerne : les `blobs`, les arbres (`trees`) et les `commits`. === Les blobs === Tout d'abord, faisons un peu de magie. Choisissez un nom de fichier... n'importe quel nom de fichier ! Puis dans un dossier vide, faites (en remplaçant `VOTRE_NOM_DE_FICHIER` par le nom que vous avez choisi) : $ echo joli > VOTRE_NOM_DE_FICHIER $ git init $ git add . $ find .git/objects -type f Vous verrez +.git/objects/06/80f15d4cb13a09f600a25b84eae36506167970+. Comment puis-je le savoir sans connaître le nom de fichier que vous avez choisi ? Tout simplement parce que l'empreinte SHA1 de : "blob" SP "5" NUL "joli" LF est 0680f15d4cb13a09f600a25b84eae36506167970. Où SP est un espace, NUL est l'octet de valeur nulle et LF est un passage à la ligne. Vous pouvez vérifier cela en tapant : $ printf "blob 5\000joli\n" | sha1sum Git utilise un classement par contenu : les fichiers ne sont pas stockés selon leur nom mais selon l'empreinte des données qu'ils contiennent, dans un fichier que nous appelons un objet 'blob'. Nous pouvons considérer l'empreinte comme un ID unique du contenu d'un fichier. Donc nous pouvons retrouver un fichier par son contenu. La chaîne initiale `blob 5` est simplement un entête indiquant le type de l'objet et sa longueur en octets ; cela simplifie le classement interne. Je peux donc aisément prédire ce que vous voyez. Le nom du fichier ne compte pas : pour construire l'objet blob, seules comptent les données stockées dans le fichier. Peut-être vous demandez-vous ce qui se produit pour des fichiers ayant le même contenu. Essayez en créant des copies de votre premier fichier, avec des noms quelconques. Le contenu de +.git/objects+ reste le même quel que soit le nombre de copies que vous avez ajoutées. Git ne stocke le contenu qu'une seule fois. À propos, les fichiers dans +.git/objects+ sont compressés par zlib et, par conséquent, vous ne pouvez pas en consulter le contenu directement. Passez-les au travers du filtre http://www.zlib.net/zpipe.c[zpipe -d] ou tapez : $ git cat-file -p 0680f15d4cb13a09f600a25b84eae36506167970 qui affiche proprement l'objet choisi. === Les arbres (`trees`) === Mais que deviennent les noms des fichiers ? Ils doivent bien être stockés quelque part à un moment. Git se préoccupe des noms de fichiers lors d'un commit : $ git commit # Tapez un message $ find .git/objects -type f Vous devriez voir maintenant trois objets. Mais là, je ne peux plus prédire le nom des deux nouveaux fichiers puisqu'ils dépendent en partie du nom de fichier que vous avez choisi. Nous continuerons en supposant que vous avez choisi ``rose''. Si ce n'est pas le cas, vous pouvez réécrire l'histoire pour que ce soit le cas : $ git filter-branch --tree-filter 'mv VOTRE_NOM_DE_FICHIER rose' $ find .git/objects -type f Le fichier +.git/objects/9a/6a950c3b14eb1a3fb540a2749514a1cb81e206+ devrait maintenant apparaître puisque c'est l'empreinte SHA1 du contenu suivant : "tree" SP "32" NUL "100644 rose" NUL 0x9a6a950c3b14eb1a3fb540a2749514a1cb81e206 Vérifiez que ce contenu est le bon en tapant : $ echo 9a6a950c3b14eb1a3fb540a2749514a1cb81e206 | git cat-file --batch Avec zpipe, il est plus simple de vérifier l'empreinte : $ zpipe -d < .git/objects/9a/6a950c3b14eb1a3fb540a2749514a1cb81e206 | sha1sum La vérification de l'empreinte est plus difficile via cat-file puisque cette commande n'affiche pas que le contenu brut du fichier après décompression. Cette fichier est un objet arbre ('tree') : une liste de tuples constitués d'un type, d'un nom de fichier et d'une empreinte. Dans notre exemple, le type est 100644 qui indique que `rose` est un fichier normal et l'empreinte est celle de l'objet de type blob contenant le contenu de `rose`. Les autres types possibles pour un fichier sont exécutable, lien symbolique ou dossier. Dans ce dernier cas, l'empreinte représente un autre objet de type arbre. Si vous faites appel à la commande filter-branch, vous verrez apparaître de vieux objets dont vous n'avez pas besoin. Même s'ils disparaîtront automatiquement une fois expirée la période de rétention, nous allons les effacer dès maintenant pour rendre notre petit exemple plus facile à suivre : $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune Sur de vrais projets, vous devriez éviter de telles commandes puisqu'elles détruisent les sauvegardes. Si vous voulez un dossier propre, il est conseillé de faire un tout nouveau clone. Faites aussi attention si vous manipulez directement le contenu de +.git+ : que se passera-t-il si une commande Git s'effectue au même moment ou si le courant est soudainement coupé ? De manière générale, les refs devraient toujours être effacées via *git update-ref -d* même si on considère comme sans risque la suppression manuelle de +refs/original+. === Les commits === Nous avons expliqué 2 des 3 types d'objets. Le troisième est l'objet 'commit'. Son contenu dépend du message de commit ainsi que de la date et l'heure auxquelles il a été créé. Pour que vous obteniez la même chose qu'ici, nous devons bidouiller un peu : $ git commit --amend -m Shakespeare # Changement de message de commit $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Trucage de la date, l'heure et l'auteur. $ find .git/objects -type f Le fichier +.git/objects/ae/9d1241b2b6eea90529149a065f6bc444365c2a+ devrait maintenant exister puisque c'est l'empreinte SHA1 du contenu suivant : "commit 158" NUL "tree 9a6a950c3b14eb1a3fb540a2749514a1cb81e206" LF "author Alice 1234567890 -0800" LF "committer Bob 1234567890 -0800" LF LF "Shakespeare" LF Comme précédemment, vous pouvez utiliser zpipe ou cat-file pour vérifier par vous-même. C'est le premier commit, ce qui explique pourquoi il n'y a pas de commit parent. Mais les commits suivants contiendront toujours au moins une ligne identifiant un commit parent. === Indiscernable de la magie === Les secrets de Git semblent trop simples. On imagine qu'il suffit de mélanger quelques scripts shell et d'y ajouter une pincée de code C pour mitonner un tel système en quelques heures : un assemblage d'opérations basiques sur les fichiers et de calcul d'empreintes SHA1 garni de quelques fichiers verrou et d'appels à fsync pour la robustesse. En fait, nous venons précisément de décrire les premières versions de Git. Malgré tout, mis à part quelques techniques astucieuses de compression pour gagner de la place et d'indexation pour gagner du temps, nous savons maintenant comment Git transforme adroitement un système de fichiers en une base de données parfaitement adaptée à de la gestion de versions. Par exemple, si un fichier quelconque de la base d'objets vient à être corrompu par une erreur disque alors son empreinte ne correspond plus et nous sommes alertés du problème. En calculant l'empreinte des empreintes d'autres objets, nous maintenons l'intégrité à tous les niveaux. Les commits sont atomiques puisque ils ne peuvent jamais mémoriser des modifications partiellement stockées : nous ne pouvons calculer l'empreinte d'un commit et le stocker dans la base d'objets qu'après y avoir déjà stocké tous les arbres, blobs et parents relatifs à ce commit. La base d'objets est immunisée contre les interruptions inattendues telles que les coupures de courant. Nous faisons même échouer les tentatives d'attaque les plus sournoises. Supposez que quelqu'un tente de modifier discrètement le contenu d'un fichier dans l'une des anciennes versions du projet. Pour rendre cohérent le contenu de la base d'objets, il lui faut changer l'empreinte de l'objet blob correspondant puisque elle doit maintenant représenter une chaîne d'octets différente. Cela signifie qu'il doit aussi changer l'empreinte de tous les arbres référençant ce blob et donc changer l'empreinte de tous les commits impliquant ces arbres ainsi que de tous les descendants de ces commits. Cela implique que l'empreinte du HEAD officiel diffère de celle du HEAD d'un dépôt corrompu. En remontant la suite d'empreintes erronées nous pouvons localiser avec précision le fichier corrompu ainsi que le premier commit où il l'a été. En résumé, tant que nous sommes sûrs des 20 octets représentant le dernier commit, il est impossible d'altérer un dépôt Git. Qu'en est-il des fameuses fonctionnalités de Git ? Des branchements ? Des fusions ? Des tags ? De simples détails. La tête courante est conservée dans le fichier +.git/HEAD+ qui contient l'empreinte d'un objet commit. Cette empreinte sera tenue à jour durant un commit ainsi que durant de nombreuses autres commandes. Les branches fonctionnent de manière similaire : ce sont des fichiers dans +.git/refs/heads+. Et les tags aussi : ils sont dans +.git/refs/tags+ mais ils sont mis à jour par un ensemble différent de commandes. // LocalWords: doc flyspell coding utf fill-column Git référez-vous commits mv // LocalWords: git cryptographiques SHA chains d'horodatage rm add // LocalWords: stat List Linux object database tags logs HEAD blobs trees echo // LocalWords: init find objects puis-je blob SP LF cb eae printf sha sum zlib // LocalWords: demandez-vous Passez-les zpipe cat-file filter-branch tree eb // LocalWords: tree-filter fb batch tuples reflog now all refs update-ref Fri // LocalWords: amend Shakespeare env-filter export AUTHOR Feb NAME Alice EMAIL // LocalWords: COMMITTER author committer shell fsync gitmagic-20160304/fr/multiplayer.txt0000644000175000017500000002537712666307504016535 0ustar sbadiasbadia// -*- mode: doc; mode: visual-line; mode: flyspell; coding: utf-8; -*- == Git multijoueur == Au départ, j'utilisais Git pour un projet privé où j'étais le seul développeur. Parmi toutes les commandes liées à la nature distribuée de Git, je n'avais besoin que de *pull* et *clone* afin de disposer de mon projet en différents lieux. Plus tard, j'ai voulu publier mon code via Git et inclure des modifications de plusieurs contributeurs. J'ai dû apprendre à gérer des projets avec de nombreux développeurs à travers le monde. Heureusement c'est l'un des points forts de Git et peut-être même sa _raison d'être_ (en français dans le texte). === Qui suis-je ? === À chaque commit sont associés le nom et le mail de l'auteur, ceux qui sont montrés par *git log*. Par défaut, Git utilise les valeurs fournies par le système pour remplir ces champs. Pour les configurer explicitement, tapez : $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com Supprimer l'option `--global` pour que ces valeurs soient locales au dépôt courant. === Git via SSH et HTTP === Supposez que vous ayez un accès SSH à un serveur Web sur lequel Git n'est pas installé. Bien que ce soit moins efficace que le protocole natif, Git sait communiquer par dessus HTTP. Télécharger, compiler et installer Git sur votre compte et créer un dépôt dans votre dossier web : $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update Avec les vieilles versions de Git, la commande de copie échoue et vous devez faire : $ chmod a+x hooks/post-update Maintenant vous pouvez transmettre vos modifications via SSH depuis n'importe lequel de vos clones : $ git push web.server:/path/to/proj.git master et n'importe qui peut récupérer votre projet grâce à : $ git clone http://web.server/proj.git === Git via n'importe quoi === Besoin de synchroniser des dépôts sans passer par un serveur ni même une connexion réseau ? Besoin d'improviser dans l'urgence ? Nous avons déjà vu que <>. Nous pourrions utiliser des fichiers de ce type pour assurer le transport entre des dépôts Git via n'importe quel canal. Mais un outil plus puissant existe : *git bundle*. L'émetteur crée un 'bundle' : $ git bundle create monbundle HEAD puis il transmet ce bundle, +monbundle+, à l'autre partie par n'importe quel moyen : email, clé USB, impression puis reconnaissance de caractères, lecture des bits au téléphone, signaux de fumée, etc. Le récepteur retrouve les mises à jour du bundle en tapant : $ git pull monbundle Le récepteur peut même faire cela dans un dépôt entièrement vide. Malgré sa petite taille +monbundle+ contient l'ensemble du dépôt Git d'origine. Pour des projets plus gros, on peut réduire le gaspillage en incluant dans le bundle uniquement les changements manquants dans l'autre dépôt. En supposant par exemple que le commit ``1b6d...'' est le commit le plus récent partagé par les deux dépôts, on peut faire : $ git bundle create monbundle HEAD ^1b6d Si on fait cela souvent, il se peut qu'on ne sache plus quel est le dernier commit partagé. La page d'aide suggère d'utiliser des tags pour résoudre ce problème. En pratique, juste après l'envoi d'un bundle, tapez : $ git tag -f dernierbundle HEAD et pour créer un nouveau bundle faites : $ git bundle create nouveaubundle HEAD ^dernierbundle === Les patches : la monnaie d'échange globale === Les patches sont des représentations textuelles de vos modifications qui peuvent être facilement compris par les ordinateurs comme par les humains. C'est ce qui leur donne leur charme. Vous pouvez envoyer un patch par mail à un développeur sans savoir quel système de gestion de versions il utilise. À partir du moment où on peut lire les mails que vous envoyez, on peut voir vos modifications. De votre côté, vous n'avez besoin que d'un compte mail : aucune nécessité de mettre en œuvre un dépôt Git en ligne. Souvenez-vous du premier chapitre. La commande : $ git diff 1b6d > mon.patch produit un patch qui peut être collé dans un mail. Dans un dépôt Git, tapez : $ git apply < mon.patch pour appliquer le patch. D'une manière plus formelle, lorsque le nom des auteurs et peut-être leur signature doit apparaître, générez tous les patches depuis un certain point en tapant : $ git format-patch 1b6d Les fichiers résultants peuvent être fournis à *git send-email* ou envoyés à la main. Vous pouvez aussi spécifier un intervalle entre deux commits : $ git format-patch 1b6d..HEAD^^ Du côté du destinataire, enregistrez un mail dans un fichier puis tapez : $ git am < mail.txt Ça appliquera le patch reçu mais créera aussi un commit en y incluant toutes les informations telles que le nom des auteurs. Si vous utilisez un client de messagerie dans un navigateur, il vous faudra sans doute appuyer sur un bouton afin de voir le mail dans son format brut avant de l'enregistrer dans un fichier. Il y a de légères différences dans le cas des clients de messagerie se basant sur le format mbox, mais si vous utilisez l'un d'entre eux, vous êtes sans aucun doute capable de vous en débrouiller facilement sans lire des tutoriels ! (NdT : si votre dépôt contient des fichiers binaires, n'oubliez-pas d'ajouter l'option +--binary+ aux commandes de création de patches ci-dessus.) === Le numéro de votre correspondant a changé === Après la création d'un clone d'un dépôt, l'utilisation de *git push* ou de *git pull* se référera automatiquement à l'URL du dépôt d'origine. Comment Git fait-il ? Le secret réside dans des options de configuration ajoutées dans le clone. Jetons-y un œil : $ git config --list L'option +remote.origin.url+ détermine l'URL de la source ; ``origin'' est un alias donné au dépôt d'origine. Comme dans le cas de la branche principale qui se nomme ``master'' par convention, on peut changer ou supprimer cet alias mais il n'y a habituellement aucune raison de le faire. Si le dépôt original change, vous pouvez modifier son URL via : $ git config remote.origin.url git://nouvel.url/proj.git L'option +branch.master.merge+ spécifie le nom de la branche distante utilisée par défaut par la commande *git pull*. Lors du clonage initial, le nom choisi est celui de la branche courant du dépôt d'origine. Même si le HEAD du dépôt d'origine est déplacé vers une autre branche, la commande pull continuera à suivre fidêlement la branche initiale. Cette option ne s'applique qu'au dépôt ayant servi au clonage initial, celui enregistré dans l'option +branch.master.remote+. Si nous effectuons un pull depuis un autre dépôt, nous devrons indiquer explicitement la branche voulue : $ git pull git://example.com/other.git master Les détails ci-dessus expliquent pourquoi nos appels à push et pull dans nos précédents exemples n'avaient pas besoin d'arguments. === Les branches distantes === Lorsque nous clonons un dépôt, nous clonons aussi toutes ses branches. Vous ne les avez sans doute pas remarquées car Git les cache : vous devez explicitement demander à les voir. Cela empêche les branches du dépôt distant d'interférer avec vos propres branches et cela rend aussi Git plus simple pour les débutants. Listons les branches distantes : $ git branch -r Vous devriez voir quelque chose comme : origin/HEAD origin/master origin/experimental Ces noms sont ceux des branches et du HEAD du dépôt distant et ils peuvent être utilisés dans les commandes Git normales. Supposez par exemple que vous avez réalisé de nombreux commits et que vous vouliez voir la différence avec la dernière version ramenée par fetch. Vous pourriez rechercher dans le log pour retrouver l'empreinte SHA1 appropriée mais il est beaucoup plus simple de tapez : $ git diff origin/HEAD Vous pouvez aussi voir où en est rendu la branche ``experimental'' : $ git log origin/experimental === Dépôts distants multiples === Supposez que deux autres développeurs travaillent sur notre projet et que nous souhaitons garder un œil sur les deux. Nous pouvons suivre plus d'un dépôt à la fois grâce à : $ git remote add un_autre git://example.com/un_depot.git $ git pull un_autre une_branche Maintenant nous avons fusionné avec une branche d'un second dépôt et nous avons accès facilement à toutes les branches de tous les dépôts : $ git diff origin/experimental^ un_autre/une_branche~5 Mais comment faire si nous souhaitons juste comparer leurs modifications sans affecter notre travail ? En d'autres termes, nous voulons examiner leurs branches sans que leurs modifications envahissent notre dossier de travail. À la place d'un pull, faisons : $ git fetch # Rapatrier depuis le dépôt d'origin, par défaut $ git fetch un_autre # Rapatrier depuis le dépôt d'un_autre Cela ne rapatrie (fetch) que les historiques. Bien que notre dossier de travail reste inchangé, nous pouvons faire référence à n'importe quelle branche de n'importe quel dépôt dans nos commandes Git puisque nous en possédons maintenant une copie locale. Rappelez-vous qu'en coulisse, un *pull* est simplement un *fetch* suivi d'un *merge*. Habituellement nous faisons appel à *pull* car nous voulons fusionner (merge) les dernières modifications distantes après les avoir rapatriées (fetch). La situation ci-dessus est une exception notable. Voir *get help remote* pour savoir comment supprimer des dépôts distants, ignorer certaines branches et bien plus encore. === Mes préférences === Pour mes projets, j'aime que mes contributeurs se confectionnent un dépôt depuis lequel je peux effectuer des pull. Certains services d'hébergement Git vous permettent de créer votre propre dépôt clone d'un projet en cliquant simplement sur un bouton. Après avoir rapatrié (fetch) un arbre de modifications, j'utilise les commandes Git pour parcourir et examiner ces modifications qui, idéalement, sont bien organisées et bien décrites. Je fusionne mes propres modifications et effectue parfois quelques modifications supplémentaires. Une fois satisfait, je les envoie (push) vers le dépôt principal. Bien que recevant rarement des contributions, je pense que mon approche est parfaitement adaptable à grande échelle. Voir http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[ce billet par Linus Torvalds]. Rester dans le monde Git est un peu plus pratique que de passer par des fichiers de patch puisque cela m'évite d'avoir à les convertir en commits Git. De plus, Git gère les détails tels qu'enregistrer le nom de l'auteur, son adresse mail ainsi que la date et l'heure et il demande à l'auteur de décrire ses propres modifications. gitmagic-20160304/fr/preface.txt0000644000175000017500000001217412666307504015562 0ustar sbadiasbadia// -*- mode: doc; mode: visual-line; mode: flyspell; coding: utf-8; fill-column: 79; -*- = Git Magique = Ben Lynn Août 2007 == Préface == http://git.or.cz/[Git] est un couteau suisse de la gestion de versions. Un outil de gestion de révisions multi-usage, pratique et fiable, dont la flexibilité en rend l'apprentissage pas si simple, sans parler de le maîtriser ! Comme Arthur C. Clarke le fait observer, toute technologie suffisamment avancée se confond avec la magie. C'est une approche intéressante pour Git : les débutants peuvent ignorer ses mécanismes internes et l'utiliser comme une baguette magique afin d'époustoufler les amis et rendre furieux les ennemis par ses fabuleuses capacités. Plutôt que de rentrer dans le détails, nous donnons des instructions pour obtenir tel ou tel effet. À force d'utilisation, petit à petit, vous comprendrez comment fonctionne chaque truc et comment composer vos propres recettes pour répondre à vos besoins. .Traductions - http://docs.google.com/View?id=dfwthj68_675gz3bw8kj[Chinois (Simplifié)] : par JunJie, Meng et JiangWei. - link:/~blynn/gitmagic/intl/fr/[Française] : par Alexandre Garel, Paul Gaborit et Nicolas Deram. Hébergé aussi chez http://tutoriels.itaapy.com/[itaapy]. - link:/~blynn/gitmagic/intl/de/[Allemande] : par Benjamin Bellee et Armin Stebich. Hébergé aussi sur http://gitmagic.lordofbikes.de/[le site web d'Armin]. - link:/~blynn/gitmagic/intl/it/[Italien] : par Mattia Rigotti. - http://www.slideshare.net/slide_user/magia-git[Portugaise] : par Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[version ODT]]. - link:/~blynn/gitmagic/intl/ru/[Russe]: par Tikhon Tarnavsky, Mikhail Dymskov et d'autres. - link:/~blynn/gitmagic/intl/es/[Espagnole] : par Rodrigo Toledo et Ariset Llerena Tapia. - link:/~blynn/gitmagic/intl/vi/[Vietnamienne]: par Trần Ngọc Quân. Hébergé aussi sur http://vnwildman.users.sourceforge.net/gitmagic.html[son site Web]. .Autres éditions - link:book.html[sur une seule page web] : HTML nu, sans CSS ; - link:book.pdf[fichier PDF] : version imprimable. - http://packages.debian.org/gitmagic[package Debian], http:://packages.ubuntu.com/gitmagic[package Ubuntu] : obtenez rapidement une copie locale de ce site. Pratique lorsque http://csdcf.stanford.edu/status/[ce serveur] est arrêté. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[un vrai livre [Amazon.com]] : 64 pages, 15.24cm x 22.86cm, noir et blanc, en anglais. Pratique en cas de panne courant. === Merci ! === Je reste modeste devant le travail fourni par tant de monde pour traduire ces pages. J'apprécie beaucoup d'élargir mon audience grâce aux efforts des personnes déjà citées. Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin et Tyler Breisacher ont contribué aux corrections et aux améliorations. François Marier maintient le paquet Debian, créé à l'origine par Daniel Baumarr. Ma gratitude va également à beaucoup d'autres pour leurs encouragements et compliments. Je suis tenté de vous citer ici, toutefois ceci risquerait de porter vos attentes à des sommets ridicules. Si par erreur je vous ai oublié, merci de me le signaler ou, plus simplement, envoyez-moi un patch ! .Hébergement Git gratuit - http://repo.or.cz/[http://repo.or.cz/] héberge des projets libres. C'est le premier hébergement Git, fondé et maintenu par les premiers développeurs Git. - http://gitorious.org/[http://gitorious.org/] est un autre site d'hébergement Git fait pour les projets Open-Source. - http://github.com/[http://github.com/] héberge des projets Open-Source gratuitement et des projets privés contre paiement. Un grand merci à ces sites pour l'hébergement de ce guide. === Licence === Ce guide est publié sous http://www.gnu.org/licenses/gpl-3.0.html[la GNU General Public License version 3]. Bien évidement, les sources sont dans un dépôt Git et peuvent être obtenues en saisissant : $ git clone git://repo.or.cz/gitmagic.git # Pour créer le dossier gitmagic ou à partir d'un des miroirs : $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git // LocalWords: Git multi-usage XXX Arthur Clarke JunJie Meng JiangWei Rodrigo // LocalWords: Toledo Bellee Alexandre Garel Potugais Leonardo Siqueira ODT web // LocalWords: Rodrigues CSS PDF imorimable Dustin Sallings Alberto Bertogli Joe // LocalWords: James Cameron Douglas Livingstone Michael Budde Albury Tarmigan // LocalWords: Derek Mahar Frode Aannevik Keith Rarick Andy Somerville Ralf Holm // LocalWords: Recker Øyvind Miklos Vajna Sébastien Hinderer Thomas Miedema git // LocalWords: Debian Baumarr envoyez-moi patch Open-Source General License Lynn // LocalWords: gitmagic mirroirs doc visual-line Ariset Llerena Tapia Armin // LocalWords: Stebich itaapy package Ubuntu Amazon.com gitmagic-20160304/fr/TODO.txt0000644000175000017500000000025112666307504014713 0ustar sbadiasbadia== Differences with EN == === clone.txt === - "this clone, so it will be ready in a flash, and you can now work on two" remove "will" for present (time concystency) gitmagic-20160304/fr/basic.txt0000644000175000017500000002241412666307504015234 0ustar sbadiasbadia// -*- mode: doc; mode: visual-line; mode: flyspell; coding: utf-8; fill-column: 79; -*- == Astuces de base == Plutôt que de plonger dans l'océan des commandes Git, utilisez ces commandes élémentaires pour commencer en vous trempant les pieds. Malgré leur simplicité, chacune d'elles est utile. En effet, lors de mon premier mois d'utilisation de Git, je ne me suis jamais aventuré au-delà de ce qui est exposé dans ce chapitre. === Enregistrer l'état courant === Vous êtes sur le point d'effectuer une opération drastique ? Avant de le faire, réalisez une capture de tous les fichiers du dossier courant : $ git init $ git add . $ git commit -m "Ma première sauvegarde" Si jamais votre opération tourne mal, vous pouvez retrouver votre version initiale, immaculée : $ git reset --hard Pour enregistrer un nouvel état : $ git commit -a -m "Une autre sauvegarde" === Ajouter, supprimer, renommer === Les commandes ci-dessus ne font que garder traces des fichiers qui étaient présents lorsque vous avez executé *git add* pour la première fois. Si vous ajoutez de nouveaux fichiers ou sous-dossiers, il faut le signaler à Git : $ git add readme.txt Documentation De même, si vous voulez que Git oublie certains fichiers : $ git rm kludge.h obsolete.c $ git rm -r incriminating/evidence/ Git supprime les fichiers pour vous si vous ne l'avez pas encore fait. Renommer un fichier revient à supprimer l'ancien nom et ajouter le nouveau. Il y a également le raccourci *git mv* qui a la même syntaxe que la commande mv. Par exemple : $ git mv bug.c feature.c === Annuler/Reprendre avancé === Parfois vous voulez seulement revenir en arrière et oublier les modifications effectuées depuis un certain temps parce qu'elles sont toutes fausses. Dans ce cas : $ git log vous montre une liste des commits récents, accompagnés de leur empreinte SHA1 : ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob Date: Tue Mar 14 01:59:26 2000 -0800 Remplacement de prinf() par write() commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alice Date: Thu Jan 1 00:00:00 1970 +0000 Commit initial ---------------------------------- Les premiers caractères de l'empreinte sont suffisants pour spécifier un commit ; ou alors, copiez et collez l'empreinte en entier. Saisissez : $ git reset --hard 766f pour restaurer l'état correspondant au commit donné et supprimer de manière permanente tous les commits plus récents de l'enregistrement. Parfois vous ne voulez faire qu'un bref saut dans un état précédent. Dans ce cas, saisissez : $ git checkout 82f5 Ceci vous ramène en arrière dans le temps, tout en conservant les commits récents. Toutefois, comme pour le voyage temporel de la science-fiction, si vous faites des modifications suivies d'un commit, vous entrerez dans une réalité parallèle puisque vos actions sont différentes de ce qu'elles étaient la première fois. Cette réalité parallèle est appelée une 'branche' ('branch'), et <>. Pour le moment rappelez-vous simplement que : $ git checkout master vous ramènera dans le présent. De plus, pour éviter que Git se plaigne, réalisez toujours un commit ou un reset de vos modifications avant de faire un checkout. Pour reprendre l'analogie du jeu vidéo : - *`git reset --hard`* : recharge une ancienne sauvegarde et supprime toutes les sauvegardes plus récentes. - *`git checkout`* : recharge une ancienne partie, mais si vous jouez avec, l'état de la partie va différer des enregistrement suivants que vous aviez réalisés la première fois. Chaque nouvelle sauvegarde sera sur une branche séparée représentant la réalité parallèle dans laquelle vous êtes entré. <>. Vous pouvez choisir de ne restaurer que certains fichiers et sous-dossiers en les nommant à la suite de la commande : $ git checkout 82f5 un.fichier un-autre.fichier Faites attention car cette forme de *checkout* peut écraser vos fichiers sans avertissement. Pour éviter les accidents, faites un commit avant toute commande checkout, surtout quand vous débutez avec Git. En général, quand vous n'êtes pas sûr des conséquences d'une opération, et pas seulement des commandes Git, faites d'abord un *git commit -a*. Vous n'aimez pas copier et coller les empreintes ? Alors utilisez : $ git checkout :/"Ma première s" pour arriver sur le commit qui commence avec ce message. Vous pouvez aussi demander la cinquième sauvegarde en arrière : $ git checkout master~5 === Reprise (revert) === Dans une cour de justice, certains évènements peuvent être effacés du procès verbal. De même vous pouvez sélectionner des commits spécifiques à défaire : $ git commit -a $ git revert 1b6d défera le dernier commit ayant cette empreinte. La reprise est enregistrée comme un nouveau commit, ce que vous pourrez constater en lançant un *git log*. === Génération du journal des modifications (changelog) === Certains projets demandent un http://en.wikipedia.org/wiki/Changelog[changelog]. Créez-le en tapant : $ git log > ChangeLog === Télécharger des fichiers === Faites une copie d'un projet géré par Git en saisissant : $ git clone git://serveur/chemin/vers/les/fichiers Par exemple, pour récupérer les fichiers utilisés pour créer ce site : $ git clone git://git.or.cz/gitmagic.git Nous aurons beaucoup à dire sur la commande *clone* d'ici peu. === Le dernier cri === Si vous avez déjà téléchargé une copie d'un projet en utilisant *git clone*, vous pouvez la mettre à jour vers la dernière version avec : $ git pull === Publication instantanée === Imaginez que vous avez écrit un script que vous voudriez partager avec d'autres. Vous pourriez leur dire de le télécharger de votre ordinateur, mais s'ils le font au moment où vous êtes en train d'améliorer le script ou d'y effectuer des modifications expérimentales, ils peuvent se retrouver dans la panade. Bien sûr, c'est pour cela qu'on a créé la publication de versions successives. Les développeurs peuvent travailler sur un projet fréquemment, mais ils ne rendent le code disponible quand lorsqu'ils le trouvent présentable. Pour faire ça avec Git, dans le dossier qui contient votre script : $ git init $ git add . $ git commit -m "Première publication" Ensuite vous pouvez dire à vos utilisateurs de lancer : $ git clone votre.ordinateur:/chemin/vers/le/script pour télécharger votre script. En considérant qu'ils ont accès à votre ordinateur via ssh. Sinon, lancez *git daemon* et dites plutôt à vos utilisateurs de lancer : $ git clone git://votre.ordinateur/chemin/vers/le/script À partir de maintenant, chaque fois que votre script est prêt à être publié, exécutez : $ git commit -a -m "Nouvelle version" et vos utilisateurs peuvent mettre à jour leur version en allant dans leur dossier contenant votre script et en saisissant : $ git pull Vos utilisateurs ne se retrouveront jamais avec une version de votre script que vous ne vouliez pas leur montrer. === Qu'ai-je fait ? === Retrouvez les modifications faites depuis le dernier commit avec : $ git diff Ou depuis hier : $ git diff "@{yesterday}" Ou entre une version spécifique et la version deux commits en arrière : $ git diff 1b6d "master~2" Dans chacun de ces cas, la sortie est un *patch* (rustine) qui peut être appliqué en utilisant *git apply*. Vous pouvez aussi essayer : $ git whatchanged --since="2 weeks ago" Souvent je parcours plutôt l'historique avec http://sourceforge.net/projects/qgit[qgit], pour sa pimpante interface photogénique, ou http://jonas.nitro.dk/tig/[tig], une interface en mode texte qui fonctionne même sur les connexions lentes. Autrement, installez un serveur web, lancez *git instaweb* et dégainez n'importe quel navigateur internet. === Exercice === Soit A, B, C, D quatre commits successifs où B est identique à A à l'exception de quelques fichiers qui ont été supprimés. Nous voudrions remettre les fichiers en D. Comment faire ? Il y a au moins trois solutions. En considérant que nous sommes à D : 1. La différence entre A et B sont les fichiers supprimés. Nous pouvons créer un patch représentant cette différence et l'appliquer : $ git diff B A | git apply 2. Vu que nous avions enregistré les fichiers en A, nous pouvons les reprendre : $ git checkout A foo.c bar.h 3. Nous pouvons aussi voir le chemin de A à B comme une modification à défaire : $ git revert B Quel est le meilleur choix ? Celui que vous préférez. C'est facile d'obtenir ce que vous voulez avec Git, et souvent il y a plein de manières de le faire. // LocalWords: doc visual-line Git git init add reset hard readme.txt rm mv log // LocalWords: kludge.h obsolete.c incriminating evidence bug.c feature.c utf // LocalWords: flyspell coding fill-column sous-dossiers commits SHA ba // LocalWords: Author Mar prinf write ea dc Alice Thu checkout branch master // LocalWords: un.fichier un-autre.fichier revert changelog Créez-le ChangeLog // LocalWords: Télécharger téléchargé télécharger ssh daemon diff yesterday // LocalWords: patch apply whatchanged since weeks ago qgit tig web instaweb // LocalWords: foo.c bar.h gitmagic-20160304/fr/grandmaster.txt0000644000175000017500000003214312666307504016462 0ustar sbadiasbadia// -*- mode: doc; mode: flyspell; coding: utf-8; fill-column: 79; -*- == La maîtrise de Git == À ce stade, vous devez être capable de parcourir les pages de *git help* et comprendre presque tout (en supposant que vous lisez l'anglais). En revanche, retrouver la commande exacte qui résoudra un problème précis peut être fastidieux. Je peux sans doute vous aider à gagner un peu de temps : vous trouverez ci-dessous quelques-unes des recettes dont j'ai déjà eu besoin. === Publication de sources === Dans mes projets, Git gère exactement tous les fichiers que je veux placer dans une archive afin de la publier. Pour créer une telle archive, j'utilise : $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === Gérer le changement === Indiquer à Git quels fichiers ont été ajoutés, supprimés ou renommés est parfois pénible pour certains projets. À la place, vous pouvez faire : $ git add . $ git add -u Git cherchera les fichiers du dossier courant et gérera tous les détails tout seul. En remplacement de la deuxième commande 'add', vous pouvez utiliser `git commit -a` pour créer un nouveau commit directement. Lisez *git help ignore* pour savoir comment spécifier les fichiers qui doivent être ignorés. Vous pouvez effectuer tout cela en une seule passe grâce à : $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove Les options *-z* et *-0* empêchent les effets secondaires imprévus dûs au noms de fichiers contenant des caractères étranges. Comme cette commande ajoutent aussi les fichiers habituellement ignorés, vous voudrez sûrement utiliser les options `-x` ou `-X`. === Mon commit est trop gros ! === Avez-vous négligé depuis longtemps de faire un commit ? Avez-vous codé furieusement et tout oublié de la gestion de versions jusqu'à présent ? Faites-vous plein de petits changements sans rapport entre eux parce que c'est votre manière de travailler ? Pas de soucis. Faites : $ git add -p Pour chacune des modifications que vous avez faites, Git vous montrera le bout de code qui a changé et vous demandera si elle doit faire partie du prochain commit. Répondez par "y" (oui) ou par "n" (non). Vous avez aussi d'autres options comme celle vous permettant de reporter votre décision ; tapez "?" pour en savoir plus. Une fois satisfait, tapez : $ git commit pour faire un commit incluant exactement les modifications qui vous avez sélectionnées (les modifications indexées). Soyez certain de ne pas utiliser l'option *-a* sinon Git fera un commit incluant toutes vos modifications. Que faire si vous avez modifié de nombreux fichiers en de nombreux endroits ? Vérifier chaque modification individuellement devient alors rapidement frustrant et abrutissant. Dans ce cas, utilisez la commande *git add -i* dont l'interface est moins facile mais beaucoup plus souple. En quelques touches vous pouvez ajouter ou retirer de votre index (voir ci-dessous) plusieurs fichiers d'un seul coup mais aussi valider ou non chacune des modifications individuellement pour certains fichiers. Vous pouvez aussi utiliser en remplacement la commande *git commit \--interactive* qui effectuera un commit automatiquement quand vous aurez terminé. === L'index : l'aire d'assemblage === Jusqu'ici nous avons réussi à éviter de parler du fameux 'index' de Git mais nous devons maintenant le présenter pour mieux comprendre ce qui précède. L'index est une aire d'assemblage temporaire. Git ne transfert que très rarement de données depuis votre dossier de travail directement vers votre historique. En fait, Git copie d'abord ces données dans l'index puis il copie toutes ces données depuis l'index vers leur destination finale. Un *commit -a*, par exemple, est en fait un processus en deux temps. La première étape consiste à construire dans l'index un instantané de l'état actuel de tous les fichiers suivis par Git. La seconde étape enregistre cet instantané de manière permanente dans l'historique. Effectuer un commit sans l'option *-a* réalise uniquement cette deuxième étape et cela n'a de sens qu'après avoir effectué des commandes qui change l'index, telle que *git add*. Habituellement nous pouvons ignorer l'index et faire comme si nous échangions directement avec l'historique. Dans certaines occasions, nous voulons un contrôle fin et nous gérons donc l'index. Nous plaçons dans l'index un instantané de certaines modifications (mais pas toutes) et enregistrons de manière permanente cet instantané soigneusement construit. === Ne perdez pas la tête === Le tag HEAD est comme un curseur qui pointe habituellement vers le tout dernier commit et qui avance à chaque commit. Certaines commandes Git vous permettent de le déplacer. Par exemple : $ git reset HEAD~3 déplacera HEAD trois commits en arrière. À partir de là, toutes les commandes Git agiront comme si vous n'aviez jamais fait ces trois commits, même si vos fichier restent dans leur état présent. Voir les pages d'aide pour quelques usages intéressants. Mais comment faire pour revenir vers le futur ? Les commits passés ne savent rien du futur. Si vous connaissez l'empreinte SHA1 du HEAD original, faites alors : $ git reset 1b6d Mais que faire si vous ne l'avez pas regardé ? Pas de panique : pour des commandes comme celle-ci, Git enregistre la valeur originale de HEAD dans un tag nommé ORIG_HEAD et vous pouvez revenir sain et sauf via : $ git reset ORIG_HEAD === Chasseur de tête === Peut-être que ORIG_HEAD ne vous suffit pas. Peut-être venez-vous de vous apercevoir que vous avez fait une monumentale erreur et que vous devez revenir à une ancienne version d'une branche oubliée depuis longtemps. Par défaut, Git conserve un commit au moins deux semaine même si vous avez demandé à Git de détruire la branche qui le contient. La difficulté consiste à retrouver l'empreinte appropriée. Vous pouvez toujours explorer les différentes valeurs d'empreinte trouvées dans `.git/objects` et retrouver celle que vous cherchez par essais et erreurs. Mais il existe un moyen plus simple. Git enregistre l'empreinte de chaque commit qu'il traite dans `.git/logs`. Le sous-dossier `refs` contient l'historique de toute l'activité de chaque branche alors que le fichier `HEAD` montre chaque valeur d'empreinte que HEAD a pu prendre. Ce dernier peut donc servir à retrouver les commits d'une branche qui a été accidentellement élaguée. La commande reflog propose une interface sympa vers ces fichiers de log. Essayez: $ git reflog Au lieu de copier/coller une empreinte listée par reflog, essayez : $ git checkout "@{10 minutes ago}" Ou basculez vers le cinquième commit précédemment visité via : $ git checkout "@{5}" Voir la section ``Specifying Revisions'' de *git help rev-parse* pour en savoir plus. Vous pouvez configurer une plus longue période de rétention pour les commits condamnés. Par exemple : $ git config gc.pruneexpire "30 days" signifie qu'un commit effacé ne le sera véritablement qu'après 30 jours et lorsque $git gc* tournera. Vous pouvez aussi désactiver le déclenchement automatique de *git gc* : $ git config gc.auto 0 auquel cas les commits ne seront véritablement effacés que lorsque vous lancerez *git gc* manuellement. === Construire au-dessus de Git === À la manière UNIX, la conception de Git permet son utilisation comme un composant de bas niveau d'autres programmes tels que des interfaces graphiques ou web, des interfaces en ligne de commandes alternatives, des outils de gestion de patch, des outils d'importation et de conversion, etc. En fait, certaines commandes Git sont de simples scripts s'appuyant sur les commandes de base, comme des nains sur des épaules de géants. Avec un peu de bricolage, vous pouvez adapter Git à vos préférences. Une astuce facile consiste à créer des alias Git pour raccourcir les commandes que vous utilisez le plus fréquemment : $ git config --global alias.co checkout $ git config --global --get-regexp alias # affiche les alias connus alias.co checkout $ git co foo # identique à 'git checkout foo' Une autre astuce consiste à intégrer le nom de la branche courante dans votre prompt ou dans le titre de la fenêtre. L'invocation de : $ git symbolic-ref HEAD montre le nom complet de la branche courante. En pratique, vous souhaiterez probablement enlever "refs/heads/" et ignorer les erreurs : $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- Le sous-dossier +contrib+ de Git est une mine d'outils construits au-dessus de Git. Un jour, certains d'entre eux pourraient être promus au rang de commandes officielles. Dans Debian et Ubuntu, ce dossier est +/usr/share/doc/git-core/contrib+. L'un des plus populaires de ces scripts est +workdir/git-new-workdir+. Grâce à des liens symboliques intelligents, ce script crée un nouveau dépôt dont l'historique est partagé avec le dépôt original. $ git-new-workdir un/existant/depot nouveau/repertoire Le nouveau dossier et ses fichiers peuvent être vus comme un clone, sauf que l'historique est partagé et que les deux arbres des versions restent automatiquement synchrones. Nul besoin de merge, push ou pull. === Audacieuses acrobaties === À ce jour, Git fait tout son possible pour que l'utilisateur ne puisse pas effacer accidentellement des données. Mais si vous savez ce que vous faites, vous pouvez passer outre les garde-fous des principales commandes. *Checkout* : des modifications non intégrées (via commit) peuvent causer l'échec d'un checkout. Pour détruire vos modifications et réussir quoi qu'il arrive un checkout d'un commit donné, utilisez l'option d'obligation : $ git checkout -f HEAD^ Inversement, si vous spécifiez des chemins particuliers pour un checkout alors il n'y a pas de garde-fous. Le contenu des chemins est silencieusement réécrit. Faites attention lorsque vous utilisez un checkout de cette manière. *Reset* : un reset échoue aussi en présence de modifications non intégrées. Pour passer outre, faites : $ git reset --hard 1b6d *Branch* : la suppression de branches échoue si cela implique la perte de certains commits. Pour forcer la suppression, tapez : $ git branch -D branche_morte # à la place de -d De manière similaire, une tentative visant à renommer une branche existante vers le nom d'une autre branche échoue si cela amène la perte de commits. Pour forcer le changement de nom, tapez : $ git branch -M source target # à la place de -m Contrairement à checkout et reset, ces deux dernières commandes n'effectuent pas la suppression des informations immédiatement. Les commits destinés à disparaître sont encore disponibles dans le sous-dossier .git et peuvent encore être retrouvés grâce aux empreintes appropriées tel que retrouvées dans `.git/logs` (voir "Chasseur de tête" ci-dessus). Par défaut, ils sont conservés au moins deux semaines. *Clean* : certaines commandes Git refusent de s'exécuter pour ne pas écraser des fichiers non suivis. Si vous êtes certain que tous ces fichiers et dossiers peuvent être sacrifiés alors effacez-les sans pitié via : $ git clean -f -d Ensuite, la commande trop prudente fonctionnera ! === Se prémunir des commits erronés === Des erreurs stupides encombrent mes dépôts. Les plus effrayantes sont dues à des fichiers manquants car oubliés lors des *git add*. D'autres erreurs moins graves concernent les espaces blancs inutiles ou les conflits de fusion non résolus : bien qu'inoffensives, j'aimerais qu'elles n'apparaissent pas dans les versions publiques. Si seulement je m'en étais prémuni en utilisant un _hook_ (un crochet) pour m'alerter de ces problèmes : $ cd .git/hooks $ cp pre-commit.sample pre-commit # Vieilles versions de Git : chmod +x pre-commit Maintenant Git empêchera un commit s'il détecte des espace inutiles ou s'il reste des conflits de fusion non résolus. Pour gérer ce guide, j'ai aussi ajouté les lignes ci-dessous au début de mon hook *pre-commit* pour me prémunir de mes inattentions : if git ls-files -o | grep '\.txt$'; then echo FAIL! Untracked .txt files. exit 1 fi Plusieurs opération de Git acceptent les hooks ; voir *git help hooks*. Nous avons déjà utilisé le hook *post-update* lorsque nous avons parlé de Git au-dessus de HTTP. Celui-ci se déclenche à chaque mouvement de HEAD. Le script d'exemple post-update met à jour les fichiers Git nécessaires à une communication au-dessus de transports agnostiques tels que HTTP. // LocalWords: doc flyspell coding utf fill-column Git git help tar prefix add // LocalWords: HEAD ls-files xargs update-index remove Faites-vous staged tag // LocalWords: reset commits SHA ORIG venez-vous refs reflog log checkout // LocalWords: ago Specifying Revisions rev-parse config gc.pruneexpire days // LocalWords: gc gc.auto run d'UNIX web patch alias.co get-regexp co foo cut // LocalWords: symbolic-ref heads contrib Debian Ubuntu workdir repertoire cd // LocalWords: git-new-workdir merge push hard Branch branch target Clean hook // LocalWords: effacez-les clean qu'inoffensives hooks cp pre-commit.sample // LocalWords: pre-commit chmod grep txt then echo FAIL Untracked post-update // LocalWords: HTTP gitmagic-20160304/fr/history.txt0000644000175000017500000003037412666307504015660 0ustar sbadiasbadia// -*- mode: doc; mode: flyspell; coding: utf-8; fill-column: 79; -*- == Les leçons de l'histoire == L'une des conséquences de la nature distribuée de Git est qu'il est facile de modifier l'historique. Mais si vous réécrivez le passé, faites attention : ne modifiez que la partie de l'historique que vous êtes le seul à posséder. Sinon, comme des nations qui se battent éternellement pour savoir qui a commis telle ou telle atrocité, si quelqu'un d'autre possède un clone dont l'historique diffère du vôtre, vous aurez des difficultés à vous réconcilier lorsque vous interagirez. Certains développeurs insistent très fortement pour que l'historique soit considéré comme immuable. D'autres pensent au contraire que les historiques doivent être rendus présentables avant d'être présentés publiquement. Git s'accommode des deux points de vue. Comme les clones, les branches et les fusions, la réécriture de l'historique est juste un pouvoir supplémentaire que vous donne Git. C'est à vous de l'utiliser à bon escient. === Je me corrige... === Que faire si vous avez fait un commit mais que vous souhaitez y attacher un message différent ? Pour modifier le dernier message, tapez : $ git commit --amend Vous apercevez-vous que vous avez oublié un fichier ? Faites *git add* pour l'ajouter puis exécutez la commande ci-dessus. Voulez-vous ajouter quelques modifications supplémentaires au dernier commit ? Faites ces modifications puis exécutez : $ git commit --amend -a === ... et bien plus === Supposons que le problème précédent est dix fois pire. Après une longue séance, vous avez effectué une série de commits. Mais vous n'êtes pas satisfait de la manière dont ils sont organisés et certains des messages associés doivent être revus. Tapez alors : $ git rebase -i HEAD~10 et les dix derniers commits apparaissent dans votre $EDITOR favori. Voici un petit extrait : pick 5c6eb73 Added repo.or.cz link pick a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile Ensuite : - Supprimez un commit en supprimant sa ligne. - Réordonnez des commits en réordonnant leurs lignes. - Remplacez `pick` par : * `edit` pour marquer ce commit pour amendement. * `reword` pour modifier le message associé. * `squash` pour fusionner ce commit avec le précédent. * `fixup` pour fusionner ce commit avec le précédent en supprimant le message associé. Sauvegardez et quittez. Si vous avez marqué un commit pour amendement alors tapez : $ git commit --amend Sinon, tapez : $ git rebase --continue Donc faites des commits très tôt et faites-en souvent : vous pourrez tout ranger plus tard grâce à 'rebase'. === Les changements locaux en dernier === Vous travaillez sur un projet actif. Vous faites quelques commits locaux puis vous vous resynchronisez avec le dépôt officiel grâce à une fusion (merge). Ce cycle se répète jusqu'au moment où vous êtes prêt à pousser vos contributions vers le dépôt central. Mais à cet instant l'historique de votre clone Git local est un fouillis infâme mélangeant les modifications officielles et les vôtres. Vous préféreriez que toutes vos modifications soient contiguës et se situent après toutes les modifications officielles. C'est un boulot pour *git rebase* comme décrit ci-dessus. Dans la plupart des cas, vous pouvez utilisez l'option *--onto* et éviter les interactions. Lisez *git help rebase* pour des exemples détaillés sur cette merveilleuse commande. Vous pouvez scinder des commits. Vous pouvez même réarranger des branches de l'arbre. === Réécriture de l'histoire === De temps en temps, vous avez besoin de faire des modifications équivalentes à la suppression d'une personne d'une photo officielle, la gommant ainsi de l'histoire d'une manière quasi Stalinienne. Supposons que vous ayez publié un projet mais en y intégrant un fichier que vous auriez dû conserver secret. Par exemple, vous avez accidentellement ajouté un fichier texte contenant votre numéro de carte de crédit. Supprimer ce fichier n'est pas suffisant puisqu'il pourra encore être retrouvé via d'anciennes versions du projet. Vous devez supprimer ce fichier dans toutes les versions : $ git filter-branch --tree-filter 'rm top/secret/fichier' HEAD La documentation *git help filter-branch* explique cette exemple et donne une méthode plus rapide. De manière générale, *filter-branch* vous permet de modifier des pans entiers de votre historique grâce à une seule commande. Après cela, le dossier +.git/refs/original+ contiendra l'état de votre dépôt avant l'opération. Vérifiez que la commande filter-branch a bien fait ce que vous souhaitiez puis effacer ce dossier si vous voulez appliquer d'autres commandes filter-branch. Finalement, remplacez tous les clones de votre projet par votre version révisée si vous voulez pouvoir interagir avec eux plus tard. === Faire l'histoire === [[makinghistory]] Voulez-vous faire migrer un projet vers Git ? S'il est géré par l'un des systèmes bien connus alors il y a de grandes chances que quelqu'un ait déjà écrit un script afin d'importer l'ensemble de l'historique dans Git. Sinon, regarder du côté de *git fast-import* qui lit un fichier texte dans un format spécifique pour créer un historique Git à partir de rien. Typiquement un script utilisant cette commande est un script jetable qui ne servira qu'une seule fois pour migrer le projet d'un seul coup. À titre d'exemple, collez le texte suivant dans un fichier temporaire (`/tmp/historique`) : ---------------------------------- commit refs/heads/master committer Alice Thu, 01 Jan 1970 00:00:00 +0000 data < int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob Tue, 14 Mar 2000 01:59:26 -0800 data < int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- Puis créez un dépôt Git à partir de ce fichier temporaire en tapant : $ mkdir projet; cd projet; git init $ git fast-import --date-format=rfc2822 < /tmp/historique Vous pouvez extraire la dernière version de ce projet avec : $ git checkout master . La commande *git fast-export* peut convertir n'importe quel dépôt Git en un fichier au format *git fast-import* ce qui vous permet de l'étudier pour écrire des scripts d'exportation mais vous permet aussi de transporter un dépôt dans un format lisible. Ces commandes permettent aussi d'envoyer un dépôt via un canal qui n'accepte que du texte pur. === Qu'est-ce qui a tout cassé ? === Vous venez tout juste de découvrir un bug dans une fonctionnalité de votre programme et pourtant vous êtes sûr qu'elle fonctionnait encore parfaitement il y a quelques mois. Zut ! D'où provient ce bug ? Si seulement vous aviez testé cette fonctionnalité pendant vos développements. Mais il est trop tard. En revanche, en supposant que vous avez fait des commits suffisamment souvent, Git peut cerner le problème. $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git extrait un état à mi-chemin entre ces deux versions (HEAD et 1b6d). Testez la fonctionnalité et si le bug se manifeste : $ git bisect bad Si elle ne se manifeste pas, remplacer "bad" (mauvais) par "good" (bon). Git vous transporte à nouveau dans un état à mi-chemin entre la bonne et la mauvaise version, en réduisant ainsi les possibilités. Après quelques itérations, cette recherche dichotomique vous amènera au commit où le bug est survenu. Une fois vos investigations terminées, retourner à votre état original en tapant : $ git bisect reset Au lieu de tester chaque état à la main, automatisez la recherche en tapant : $ git bisect run mon_script Git utilise la valeur de retour du script fourni pour décider si un état est bon ou mauvais : mon_script doit retourner 0 si l'état courant est ok, 125 si cet état doit être sauté et n'importe quelle valeur entre 1 et 127 si l'état est mauvais. Une valeur négative abandonne la commande bisect. Vous pouvez faire bien plus : la page d'aide explique comment visualiser les bisects, comment examiner ou rejouer le log d'un bisect et comment éliminer des changements que vous savez sans conséquence afin d'accélérer la recherche. === Qui a tout cassé ? === Comme de nombreux systèmes de gestion de versions, Git a sa commande blame : $ git blame bug.c Cette commande annote chaque ligne du fichier afin de montrer par qui et quand elle a été modifiée la dernière fois. À l'inverse de la plupart des autres systèmes, cette commande marche hors-ligne et ne lit que le disque local. === Expérience personnelle === Avec un système de gestion de versions centralisé, la modification de l'historique est une opération difficile et faisable uniquement par les administrateurs. Créer un clone, créer une branche ou en fusionner plusieurs sont des opérations impossibles à réaliser sans communication réseau. Il en est de même pour certains opérations basiques telles que parcourir l'historique ou intégrer une modification. Avec certains systèmes, des communications réseaux sont même nécessaires juste pour voir ses propres modifications ou pour ouvrir un fichier avec le droit de modification. Ces systèmes centralisés empêchent le travail hors-ligne et nécessitent une infrastructure réseau d'autant plus lourde que le nombre de développeurs augmentent. Plus important encore, certaines opérations deviennent si lentes que les utilisateurs les évitent à moins qu'elles soient absolument indispensables. Dans les cas extrêmes cela devient vrai même pour les commandes les plus basiques. Lorsque les utilisateurs doivent effectuer des opérations lentes, la productivité souffre des interruptions répétées. J'ai moi-même vécu ce phénomène. Git a été le premier système de gestion de versions que j'ai utilisé. Je me suis vite accoutumé à lui, tenant la plupart de ses fonctionnalités pour acquises. Je pensais que les autres systèmes étaient similaires : le choix d'un système de gestion de versions ne devait pas être bien différent du choix d'un éditeur de texte ou d'un navigateur web. J'ai été très surpris lorsque, plus tard, il m'a fallu utilisé un système centralisé. Une liaison internet épisodique importe peu avec Git mais rend le développement quasi impossible lorsque le système exige qu'elle soit aussi fiable que les accès au disque local. De plus, je me restreignais afin d'éviter certaines commandes trop longues, ce qui m'empêchait de suivre ma méthode de travail habituelle. Lorsqu'il me fallait utiliser ces commandes lentes, cela interrompait mes réflexions et avait des effets pervers. En attendant la fin des communications avec le serveur, je me lançais dans autre chose pour passer le temps comme lire mes mails ou écrire de la documentation. Lorsque je revenais à mon travail initial, la commande s'était terminée depuis longtemps et je perdais du temps à retrouver le fil de mes pensées. Les être humains ne sont pas bons pour changer de contexte. Il y a aussi un effet intéressant du type « tragédie des biens communs » : afin d'anticiper la congestion du réseau, certains vont consommer plus de bandes passantes que nécessaire pour effectuer des opérations visant à réduire leurs attentes futures. Ces efforts combinés vont encore augmenter la congestion, incitant ces personnes à consommer encore plus de bande passante pour éviter ces délais toujours plus longs. // LocalWords: Git doc visual-line flyspell coding utf git amend add commits // LocalWords: apercevez-vous Voulez-vous rebase HEAD EDITOR pick eb Added How // LocalWords: link Reordered Work You Want push target to Makefile edit fixup // LocalWords: reword faites-en merge onto help filter-branch tree-filter rm // LocalWords: filter-banch makinghistory fast-import refs committer Alice Thu // LocalWords: EOT inline hello.c include stdio.h int printf world return Mar // LocalWords: write unistd.h mkdir cd init date-format rfc checkout master ok // LocalWords: fast-export bug bisect start bad good reset run bisects log web // LocalWords: blame bug.c gitmagic-20160304/fr/translate.txt0000644000175000017500000000335012666307504016146 0ustar sbadiasbadia// -*- mode: doc; mode: flyspell; coding: utf-8; fill-column: 79; -*- == Appendix B: Traduire ce guide == Faites un clone du source puis créer un répertoire correspondant au http://www.iana.org/assignments/language-subtag-registry[code IETF de la langue souhaitée] : voir http://www.w3.org/International/articles/language-tags/Overview.en.php[l'article du W3C concernant l'internationalisation]. Par exemple pour l'anglais c'est "en", pour le japonais c'est "ja" et pour le chinois traditionnel c'est "zh-Hant". Dans ce nouveau répertoire, traduisez chacun des fichiers +txt+ du répertoire "en" original. Par exemple, pour créer ce guide en http://fr.wikipedia.org/wiki/Klingon%20(langue)[Klingon], vous devriez faire : $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" est le code IETF de la langue Klingon. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Traduire le fichier. et ainsi de suite pour tous les fichiers. Vous pouvez relire votre travail incrémentalement : $ make LANG=tlh $ firefox book.html Faites souvent des commits pour vos modifications puis faites-le moi savoir dès que c'est prêt. GitHub.com propose une interface qui facilite les choses : faites un fork du projet "gitmagic", poussez-y vos modifications et demandez-moi de les fusionner. J'aime avoir des traductions qui suivent le schéma ci-dessus car mes scripts peuvent alors produire les versions HTML et PDF. Et puis c'est pratique de conserver toutes les traductions dans le dépôt officiel. Mais que cela ne vous empêche pas de faire ce qui vous convient le mieux : par exemple, les traducteurs chinois préfèrent utiliser Google Docs. Je suis content tant que votre travail permet à d'autres de profiter de mon travail. gitmagic-20160304/fr/intro.txt0000644000175000017500000002074312666307504015311 0ustar sbadiasbadia// -*- mode: doc; mode: visual-line; mode: flyspell; coding: utf-8; fill-column: 79; -*- == Introduction == Je vais me servir d'une analogie pour présenter la gestion de versions. Référez-vous à http://fr.wikipedia.org/wiki/Gestion_de_versions[la page de wikipedia sur la gestion de versions] pour une explication plus censée. === Le travail comme un jeu === J'ai joué à des jeux vidéos presque toute ma vie. Par contre, je n'ai commencé à utiliser des systèmes de gestion de versions qu'à l'âge adulte. Je pense ne pas être le seul dans ce cas et la comparaison entre les deux peut rendre les concepts plus simples à expliquer et à comprendre. Pensez à l'édition de votre code, ou de votre document, comme s'il s'agissait de jouer à un jeu. Quand vous avez bien progressé, vous aimeriez faire une sauvegarde. Pour cela vous cliquez sur le bouton 'enregistrer' de votre fidèle éditeur. Mais ceci va écraser l'ancienne version. C'est comme ces anciens jeux qui n'avaient qu'un emplacement : oui vous pouviez faire une sauvegarde mais vous ne pouviez pas revenir dans un état précédent. Quel dommage, vu que votre sauvegarde précédente pouvait éventuellement être située à un passage du jeu particulièrement amusant sur lequel vous seriez bien revenu un de ces jours. Ou, encore pire, votre seule sauvegarde était dans un état qui ne permettait pas de gagner et vous deviez tout recommencer à zéro. === Gestion de versions === Lorsque vous modifiez un document, dans le but de conserver les anciennes versions, vous pouvez l'"Enregistrer Sous..." un nom de fichier différent ou le recopier ailleurs avant de l'enregistrer. Vous pouvez même compresser ces copies pour gagner de l'espace. C'est une forme primitive et laborieuse de gestion de versions. Les jeux vidéo se sont améliorés sur ce point depuis longtemps pusique la plupart proposent différents emplacements de sauvegarde automatiquement horodatés. Rendons le problème légèrement plus coriace. Imaginez que vous ayez un ensemble de fichiers qui vont ensemble comme le code source d'un projet ou les fichiers d'un site web. Dans ce cas si vous voulez conserver une ancienne version, vous devez archiver le dossier en entier. Conserver un grand nombre de versions à la main n'est pas pratique et devient rapidement fastidieux. Dans le cas de certains jeux vidéo, l'enregistrement d'une partie est réellement constitué d'un dossier rempli de fichiers. Ces jeux cachent ce détail au joueur et présentent une interface adaptée pour gérer différentes versions de ce dossier. Les systèmes de gestion de versions ne font pas autre chose. Ils offrent tous une belle interface pour gérer un dossier rempli de plein de choses. Vous pouvez enregistrer l'état du dossier aussi souvent que vous voulez et, plus tard, vous pouvez recharger l'un des états enregistrés. À la différence de la plupart des jeux vidéos, ils sont généralement habiles pour économiser l'espace nécessaire. Typiquement, seuls quelques fichiers changent d'une version à une autre, et pas de beaucoup. Stocker ces différences au lieu des nouvelles copies complètes économise de l'espace. === Gestion distribuée === Imaginez maintenant un jeu vidéo très difficile. Si difficile à terminer que plein de joueurs expérimentés de toute la planète décident de faire équipe et de partager leurs parties enregistrées pour essayer d'en venir à bout. http://fr.wikipedia.org/wiki/Speedrun[Les Speedruns] en sont un exemple concret : des joueurs qui se spécialisent dans différents niveaux du même jeu collaborent pour produire des résultats surprenants. Quel système mettriez-vous en place pour qu'ils puissent accéder facilement aux sauvegardes des uns et des autres ? Et pour qu'ils puissent en téléverser de nouvelles ? Dans le passé, tous les projets utilisaient une gestion de versions centralisée. Quelque part un serveur contenait l'ensemble des sauvegardes du jeu et personne d'autre. Chaque joueur conservait au plus quelques sauvegardes de parties sur leur machine. Quand un joueur voulait progresser, il téléchargeait les dernières sauvegardes du serveur, jouait un moment, puis sauvegardait et téléversait ses nouvelles sauvegardes vers le serveur pour les mettre à disposition de tous les autres. Qu'en était-il si pour une raison quelconque, des joueurs voulaient obtenir une partie enregistrée antérieurement ? Peut-être la sauvegarde actuelle de la partie était-elle dans un état sans possibilité de victoire parce que quelqu'un avait oublié de prendre un objet au niveau trois, et voulaient-ils retrouver la dernière partie enregistrée au moment où la partie pouvait encore être gagnée. Ou peut-être souhaitaient-ils comparer deux parties enregistrées précédemment pour voir le travail réalisé par un joueur précis. Il peut y avoir de nombreuses raisons de vouloir récupérer une ancienne version, mais le résultat est le même : ils devaient demander au serveur central cette partie précédemment sauvegardée. Et plus ils voulaient de parties sauvegardées, plus ils devaient communiquer. La nouvelle génération des systèmes de gestion de versions, dont Git fait partie, sont dits systèmes distribués et peuvent être vus comme une généralisation des systèmes centralisés. Quand les joueurs téléchargent du serveur principal ils obtiennent toutes les parties sauvegardées, pas uniquement la dernière. C'est comme s'ils faisaient un http://fr.wikipedia.org/wiki/Site_miroir[miroir] du serveur central. Cette opération initiale de clonage peut être coûteuse, surtout s'il y a un long historique, mais ça paie sur le long terme. Un avantage immédiat est que lorsqu'on désire une partie enregistrée, quelle qu'en soit la raison, aucune communication avec le serveur central n'est nécessaire. === Une superstition idiote === Un croyance populaire veut que les systèmes distribués ne soient pas adaptés aux projets qui ont besoin d'un dépôt central officiel. Rien n'est moins vrai. Photographier quelqu'un n'a jamais eu pour effet de voler son âme. De même, cloner le dépôt principal ne diminue pas son importance. Une première approximation assez bonne est que tout ce qu'un système centralisé de gestion de versions peut faire, un système distribué bien conçu peut le faire en mieux. Les ressources réseau sont simplement plus coûteuses que les ressources locales. Bien que nous verrons plus loin qu'il peut y avoir quelques inconvénients à l'approche distribuée, il y a moins de risques de faire des comparaisons erronées en utilisant cette approximation. Un petit projet peut ne nécessiter qu'une fraction des fonctionnalités offertes par un tel système, mais utiliser un système qui n'autorise pas les changements d'échelle pour les petits projets c'est comme utiliser les chiffres romains pour les calculs sur des petits nombres. De plus, votre projet peut grossir au-delà de vos prévisions initiales. Utiliser Git même pour les cas simples est comparable au fait d'avoir sur soi un couteau Suisse que vous utilisez surtout pour déboucher des bouteilles. Le jour où vous avez besoin d'un tournevis vous êtes content d'avoir plus qu'un simple tire-bouchon. === Conflits fusionnels === Pour aborder ce sujet, notre analogie avec le jeu vidéo serait trop tirée par les cheveux. En revanche, revenons au cas de l'édition d'un document. Imaginons que Alice insère une ligne au début d'un fichier, et que Bob en ajoute une à la fin de sa propre copie. Ils envoient tout les deux leurs modifications. La plupart des systèmes vont automatiquement déduire un traitement raisonnable des actions : accepter et fusionner leur modifications, ainsi les modifications de Alice et de Bob sont appliquées. Maintenant imaginez que Alice et Bob ont fait des modifications différentes sur la même ligne. Il devient alors impossible de procéder sans intervention humaine. Celui qui envoie ses modifications en second est informé d'un _conflit de fusion_ (_merge conflict_), et doit choisir l'une des deux versions de la ligne, ou revoir complètement cette ligne. Des situations plus complexes peuvent se présenter. Les systèmes de gestion de versions s'occupent eux même des cas les plus simples, et laissent les cas difficiles aux humains. Généralement leur comportement est configurable. // LocalWords: doc visual-line Référez-vous wikipedia cliquez web Speedruns Git // LocalWords: mettriez-vous téléverser téléchargeait téléversait veulent-ils // LocalWords: téléchargent clonage fusionnels Alice merge conflict configurable gitmagic-20160304/fr/clone.txt0000644000175000017500000003016312666307504015253 0ustar sbadiasbadia// -*- mode: doc; mode: flyspell; coding: utf-8; fill-column: 79; -*- == Clonons gaiement == Avec les anciens systèmes de gestion de versions, l'opération standard pour obtenir des fichiers est le checkout. Vous obtenez un ensemble de fichiers correspondant à un état particulier précédemment enregistré. Avec Git et d'autres systèmes distribués de gestion de versions, le clonage est l'opération standard. Pour obtenir des fichiers, on crée un 'clone' du dépôt entier. En d'autres termes, il s'agit de faire un http://fr.wikipedia.org/wiki/Site_miroir[miroir] du serveur central. Tout ce qui peut se faire sur le dépôt central peut être fait sur le vôtre. === Synchronisation entre machines === Je peux imaginer faire des archives *tar* ou utiliser *rsync* pour des sauvegardes ou une synchronisation simple. Mais parfois j'édite sur mon portable, d'autres fois sur mon fixe, et les deux peuvent ne pas avoir communiqué entre temps. Initialisez un dépôt Git et faites un *commit* de vos fichiers depuis une machine. Ensuite sur l'autre : $ git clone autre.ordinateur:/chemin/vers/les/fichiers pour créer une deuxième copie de ces fichiers et du dépôt Git. À partir de ce moment, $ git commit -a $ git pull autre.ordinateur:/chemin/vers/les/fichiers HEAD ira chercher l'état des fichiers sur l'autre ordinateur pour mettre à jour celui sur lequel vous travaillez. Si récemment vous avez fait des modifications d'un même fichier en conflit entre elles, Git vous le signalera et vous devrez répéter à nouveau le commit après avoir résolu ces conflits. === Gestion classique des sources === Initialisez le dépôt Git de vos fichiers : $ git init $ git add . $ git commit -m "Commit initial" Sur le serveur central, initialisez un 'dépôt nu' (*bare* dans la terminologie Git) dans un dossier quelconque : $ mkdir proj.git $ cd proj.git $ git init --bare $ # variante en une ligne : GIT_DIR=proj.git git init Si besoin, démarrez le démon (service) : $ git daemon --detach # peut être tourne-t-il déjà Pour les services d'hébergement en ligne, suivez les instructions fournies pour mettre en place le dépôt Git initialement vide. En général il s'agit de remplir un formulaire sur une page web. 'Poussez' votre projet vers le serveur central en utilisant : $ git push git://serveur.central/chemin/du/proj.git HEAD Pour obtenir les sources, un développeur saisit : $ git clone git://serveur.central/chemin/du/proj.git Après avoir fait des modifications, le développeur les enregistre en local : $ git commit -a Pour se mettre à jour par rapport à la dernière version : $ git pull Tout conflit lors de la fusion doit être résolu puis validé : $ git commit -a Pour envoyer les modifications locales vers le dépôt central : $ git push Si le serveur principal a de nouvelles modifications dues à d'autres développeurs, l'envoi échoue et le développeur doit se mettre à jour de la dernière version, résoudre les éventuels conflits de fusion, puis essayer à nouveau. === Dépôts nus === Un dépôt nu (*bare repository*) est nommé ainsi car il n'a pas de dossier de travail : il ne contient que des fichiers qui sont normalement cachés dans le sous dossier `.git`. En d'autres termes, il ne conserve que l'historique d'un projet et ne contient jamais le rendu d'une version donnée. Un dépôt nu joue un rôle similaire à celui du serveur principal dans un système de gestion de versions centralisé : le réceptacle de vos projets. Les développeurs clonent vos projets à partir de celui-ci et y poussent les dernières modifications officielles. En général il est placé sur un serveur qui ne fait quasiment que ce travail de distribution de l'information. Le développement s'opère sur les clones de sorte que le dépôt principal peut se passer d'un dossier de travail. Beaucoup des commandes de Git échouent sur un dépôt nu tant que la variable d'environnement `GIT_DIR` n'est pas renseignée avec le chemin vers le dépôt ou que l'option `--bare` n'est pas utilisée. === Envoi vs rapatriement (push vs pull) === Pourquoi a-t-on introduit la commande `push` (_pousser_ ou _envoyer_) au lieu de se contenter de la commande `pull` (_tirer_ ou _rapatrier_) plus familière ? Premièrement, la commande `pull` échoue sur un dépôt nu : il faut y utiliser la commande `fetch` dont nous parlerons plus tard. Mais même si nous conservions un dépôt standard sur le serveur central, y rapatrier les modifications serait peu pratique. Nous devrions d'abord nous connecter au serveur et donner en argument à la commande `pull` l'adresse de la machine depuis laquelle nous souhaitons rapatrier des modifications. Des pare-feux peuvent éventuellement nous embêter, et comment faire si nous n'avons pas d'accès `shell` au serveur ? Quoi qu'il en soit, ce cas mis à part, nous décourageons l'envoi ( en comparaison du rapatriement ) parce que cela peut entraîner des confusions lorsque la destination possède un dossier de travail. En résumé, pendant votre phase d'apprentissage de Git, n'utilisez l'envoi ( push ) que lorsque la destination est un dépôt nu ; sinon rapatriez ( pull ). === Forker un projet === Vous en avez marre de la manière dont est géré un projet ? Vous pensez pouvoir faire mieux ? Dans ce cas, sur votre serveur : $ git clone git://serveur.principal/chemin/vers/les/fichiers Ensuite, informez tout le monde du fork de ce projet sur votre serveur. Par la suite, vous pouvez fusionner les modifications venant du projet originel grâce à : $ git pull === Système ultime de sauvegarde === Vous voulez des archives redondantes et géographiquement distribuées, permettant de faire face à un désastre ? Si votre projet a beaucoup de développeurs, ne faites rien ! Chaque clone de votre code est de fait une sauvegarde. Non seulement de l'état actuel, mais de l'historique complet. Grâce aux empreintes cryptographiques, si le clone de quelqu'un est corrompu, il sera repéré dès qu'il tentera de communiquer avec d'autres. Si votre projet n'est pas si populaire, trouvez autant de serveurs que possible afin d'héberger vos clones. Le vrai paranoïaque devrait toujours noter la dernière empreinte SHA1 de 20 octets du HEAD dans un endroit sûr. Ce doit être sûr, pas privé. Par exemple, la publier dans un quotidien marcherait bien, parce qu'il est difficile de réaliser une attaque modifiant l'ensemble des exemplaires d'un journal. === Le multi-tâche à la vitesse de la lumière === Imaginons que vous souhaitiez travailler sur plusieurs fonctionnalités en parallèle. Dans ce cas validez (`commit`) votre projet et lancez : $ git clone . /un/nouveau/dossier Grâce aux http://fr.wikipedia.org/wiki/Lien_mat%C3%A9riel[liens matériels], les clones locaux sont créés plus rapidement et occupent moins de place que de simples copies. Vous pouvez maintenant travailler simultanément sur deux fonctionnalités indépendantes. Par exemple vous pouvez modifier l'un des clones pendant que l'autre est en cours de compilation. À tout moment vous pouvez valider (`commit`) vos modifications puis rapatrier (`pull`) les modifications depuis l'autre clone. $ git pull /mon/autre/clone HEAD === Guérilla de la gestion de versions === Alors que vous travaillez sur un projet qui utilise un autre système de gestion de versions, Git vous manque ? Dans ce cas, initialisez un dépôt Git dans votre dossier de travail. $ git init $ git add . $ git commit -m "Commit initial" puis clonez-le : $ git clone . /un/nouveau/dossier Allez ensuite dans le nouveau dossier et travaillez plutôt là, utilisant Git comme vous le voulez. De temps à autre, quand vous voulez vous synchroniser avec les autres, rendez-vous dans le dossier de départ, synchronisez-le en utilisant l'autre système de gestion de version, puis saisissez : $ git add . $ git commit -m "Synchro avec les autres" Ensuite allez dans le nouveau dossier et lancez : $ git commit -a -m "Description de mes modifications" $ git pull La procédure pour partager vos modifications avec les autres dépend de l'autre système de gestion de versions. Le nouveau dossier contient les fichiers avec vos modifications. Lancez toute commande de l'autre système de gestion de versions nécessaire pour les envoyer au dépôt central. Subversion, qui est peut être le meilleur système de gestion de versions centralisé est utilisé par d'innombrables projets. La commande *git svn* automatise la procédure ci-dessus pour les dépôts Subversion, et peut aussi être utilisée pour exporter un projet Git vers un dépôt Subversion. === Mercurial === Mercurial est un système de gestion de versions similaire qui peut travailler quasiment sans heurt avec Git. Avec le plugin `hg-git` un utilisateur de Mercurial peut, sans rien perdre, envoyer (push) vers ou rapatrier (pull) depuis un dossier Git. Téléchargez le plugin `hg-git` avec Git : $ git clone git://github.com/schacon/hg-git.git ou Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ Malheureusement, il ne semble pas y avoir de plugin analogue pour Git. Pour cette raison, il semble préférable d'utiliser Git plutôt que Mercurial pour le dépôt principal. Avec un dépôt Mercurial, il faut généralement un volontaire qui maintienne un dépôt Git en parallèle alors que, grâce au plugin `hg-git`, un dépôt Git fait l'affaire même pour les utilisateurs de Mercurial. Bien que ce plugin puisse convertir un dépôt Mercurial en un dépôt Git en le poussant dans un dépôt vide, cette tâche est plus simple avec le script `hg-fast-export-git`, disponible via : $ git clone git://repo.or.cz/fast-export.git Pour faire une conversion, dans un nouveau dossier : $ git init $ hg-fast-export.sh -r /depot/hg ceci après avoir ajouté le script à votre `$PATH`. === Bazaar === Nous allons rapidement évoquer Bazaar parce que c'est le système de gestion de versions distribué libre le plus populaire après Git et Mercurial. Bazaar à l'avantage d'avoir plus de recul, étant donné qu'il est relativement jeune ; ses concepteurs ont pu tirer les leçons du passé et éviter des petits écueils historiques. De plus ses développeurs ont le souci de la portabilité et de l'interopérabilité avec les autres systèmes de gestion de versions. Un plugin `bzr-git` permet aux utilisateurs de Bazaar de travailler avec les dépôts Git dans une certaine mesure, et permet de le faire de manière incrémentale, tandis que `bzr-fast-export` est fait pour les conversions uniques. === Pourquoi j'utilise Git === Au départ j'ai choisi Git parce que j'ai entendu qu'il gérait l'inimaginablement ingérable source du noyaux Linux. Je n'ai jamais ressenti le besoin d'en changer. Git m'a rendu de fiers services et je ne me suis toujours pas heurté à ses limites. Comme j'utilise surtout Linux, les éventuels problèmes sur d'autres plateformes n'entrent pas en ligne de compte. De plus je préfère les programmes C et les scripts bash aux exécutables comme les scripts Pythons : il y a moins de dépendances et je suis accro aux temps d'exécution rapides. J'ai réfléchi à la manière d'améliorer Git, allant jusqu'à écrire mes propres outils de type Git, mais uniquement à des fins de recherche. Même si j'avais terminé ce projet j'aurais tout de même continué avec Git vu que les avantages sont trop peu significatifs pour justifier l'utilisation d'un système farfelu. Bien sur, vos besoins et envies diffèrent sûrement et vous pouvez très bien vous trouver mieux avec un autre système. Néanmoins vous ne pouvez pas faire une grosse erreur en choisissant Git. // LocalWords: visual-line checkout Git doc Clonons clonage synchros tar rsync // LocalWords: git HEAD init add bare mkdir proj.git cd DIR daemon detach web // LocalWords: tourne-t-il push repository clonent fetch pare-feux shell Forker // LocalWords: fork cryptographiques SHA multi-tâche clonez-le synchronisez-le // LocalWords: Synchro svn Mercurial plugin hg-git Téléchargez hg-fast-export.sh // LocalWords: hg-fast-export-git PATH Bazaar portabilité l'interopérabilité // LocalWords: bzr-git incrémentale bzr-fast-export l'inimaginablement ingérable // LocalWords: Linux plateformes bash accro gitmagic-20160304/book.css0000644000175000017500000000347712666307504014457 0ustar sbadiasbadiabody { font-size: 95%; font-family: 'Open Sans', sans-serif; } tt, code, pre, .type { font-family: andale mono, courier new, courier, monospace; font-size: 90%; } pre { color: #0000aa; } ul li p { margin: 0; padding: 0; } /* Based on http://phrogz.net/CSS/columns3.html */ div.toc { float: left; margin: 0; padding: 0; padding-top: 0.5em; border: 0; width: 16em; background-color: #f9f9f9; margin-right:1em; } div.content { margin: 0; padding: 0; /* won't match if font is smaller in toc */ border-left: 16em solid #f9f9f9; padding-left: 1em; } div.content:after { content:' '; clear:both; display:block; height:0; overflow:hidden } div.footer { clear:left; padding: 0.5em; /* background-color: #f9f9f9; border: 1px solid #aaaaaa; */ font-size: 80%; margin: 0; } div.toc ul { list-style: none; padding: 0; margin: 0; } div.toc li ul a, li ul span.currentlink { font-weight: normal; font-size: 90%; padding-left: 2em; } div.toc a, span.currentlink{ display:block; text-decoration: none; padding-left: 0.5em; color: #0000aa; } span.currentlink { text-decoration: none; background-color: #aaaaf9; } div.toc a:visited { color: #0000aa; } div.toc a:hover { background-color: #f9f9aa; } .programlisting, .screen { margin: 0; border: 1px solid #aaaaaa; background-color: #f9f9f9; padding: 0.17em; margin: 1em; margin-right: 3em; } .parameter { font-style: italic; } h1, h2 { padding-top: 0.5em; padding-bottom: 0.17em; margin: 0; font-weight: normal; color: black; border-bottom: 1px solid #aaaaaa; } h1 { font-size: 188%; } div.chapter h2 { font-size: 188%; } div.section h2 { font-size: 150%; } gitmagic-20160304/uk/0000755000175000017500000000000012666307504013417 5ustar sbadiasbadiagitmagic-20160304/uk/drawbacks.txt0000644000175000017500000002744612666307504016136 0ustar sbadiasbadia== Додаток A: Недоліки Git == Є деякі проблеми Git, які я сховав під сукно. Деякі з них можна легко вирішити за допомогою скриптів і хуків, деякі вимагають реорганізації або перегляду проекту, а кілька неприємностей, що залишилися, доведеться потерпіти. А ще краще — взятися за них і вирішити! === Слабкі сторони SHA1 === Із часом криптографи виявляють все більше і більше слабких сторін в SHA1. Вже зараз виявлення колізій хешів здійсненно для добре фінансованої організації. Через роки, можливо, навіть типовий ПК буде мати достатню обчислювальну потужність, щоб непомітно зіпсувати сховище Git. Сподіваюся, Git перейде на кращу хеш-функцію перш ніж подальші дослідження знищать SHA1. === Microsoft Windows === Git на Microsoft Windows може бути громіздким: - http://cygwin.com/[Cygwin], Linux-подібне середовище для Windows, яке містить http://cygwin.com/packages/git/[порт Git на Windows]. - http://code.google.com/p/msysgit/[Git на MSys], варіант, що вимагає мінімальної рантайм підтримки, хоча деякі команди потребують доопрацювання. === Незв'язані файли === Якщо ваш проект дуже великий і містить багато незв'язаних файлів, які постійно змінюються, Git може опинитися в невигідному становищі порівняно з іншими системами, оскільки окремі файли не відстежуються. Git відстежує зміни всього проекту, що зазвичай буває вигідним. Вирішення - розбити проект на частини, кожна з яких складається з взаємозв'язаних файлів. Використовуйте *git submodule* якщо ви все ж хочете тримати все в одному сховищі. === Хто і що редагував? === Деякі системи управління версіями змушують вас явним чином позначити файл перед редагуванням. Хоча такий підхід особливо дратує, коли має на увазі роботу з центральним сервером, проте він має дві переваги: 1. Diff'и швидкі, оскільки потрібно перевірити тільки зазначені файли. 2. Можна виявити, хто ще працює з цим файлом, запитавши центральний сервер хто відзначив його для редагування. За допомогою відповідних скриптів, ви можете добитися того ж з Git. Це вимагає співпраці з боку іншого програміста, який повинен запускати певний скрипт при редагуванні файлу. === Історія файлу === Оскільки Git записує зміни всього проекту, відтворення історії одиничного файлу вимагає більше роботи, ніж в системах керування версіями, що слідкують за окремими файлами. Втрати як правило незначні, і це непогана ціна за те, що інші операції неймовірно ефективні. Наприклад, `git checkout` швидше, ніж `cp -a`, а дельта всього проекту стискається краще, ніж колекція пофайлових дельт. === Початкове клонування === Створення клону сховища дорожче звичайного чекаута в інших системах керування версіями при довгій історії. Початкова ціна окупається в довгостроковій перспективі, оскільки більшість наступних операцій будуть швидкими і автономними. Однак у деяких ситуаціях може бути кращим створення дрібних клонів з опцією `--depth`. Це набагато швидше, але в отриманого клону буде урізана функціональність. === Мінливі Проекти === Git був написаний, щоб бути швидким при відносно невеликих змінах. Люди вносять незначні правки від версії до версії. Однорядкове виправлення помилки тут, нова функція там, виправлені коментарі тощо. Але якщо ваші файли радикально розрізняються в сусідніх ревізіях, то з кожним коммітом ваша історія неминуче збільшиться на розмір всього проекту. Ніяка система керування версіями нічого не може з цим зробити, але користувачі Git страждають більше, оскільки зазвичай історії клонуються. Причини, з яких ці ​​зміни настільки великі, потрібно вивчити. Можливо, треба змінити формати файлів. Невеликі виправлення повинні приводити до невеликих змін не більш ніж в декількох файлах. Можливо, вам була потрібна база даних або система резервного/архівного копіювання, а не система керування версіями. Наприклад, управління версіями може бути погано пристосоване для роботи з фотографіями періодично одержуваними з веб-камери. Якщо файли дійсно повинні постійно змінюватися і при цьому версіонуватися, може мати сенс використовувати Git централізованим чином. Можна створювати дрібні клони, з невеликою історією або без історії взагалі. Звичайно, багато інструментів Git будуть недоступні, і виправлення доведеться подавати у вигляді патчів. Можливо, це і добре, тому що неясно, навіщо комусь знадобиться історія вкрай нестабільних файлів. Інший приклад — це проект, залежний від прошивки, приймаючої форму величезного двійкового файлу. Її історія нецікава користувачам, а поновлення погано стискаються, тому ревізії прошивки будуть невиправдано роздувати розмір сховища. У цьому випадку вихідний код варто тримати в сховищі Git, а бінарні файли — окремо. Для спрощення життя можна поширювати скрипт, який використовує Git для клонування коду та rsync або дрібний клон Git для прошивки. === Глобальний лічильник === Деякі централізовані системи управління версіями містять натуральне число, що збільшується при надходженні нового комміта. Git ідентифікує зміни по їх хешам, що краще в багатьох обставинах. Але деяким людям подобаються ці цілі числа всюди. На щастя, легко написати такий скрипт, щоб при кожному оновленні центральне сховище Git збільшувало ціле число, можливо у тезі, і пов'язувало його з хешем останнього комміта. Кожен клон може підтримувати такий лічильник, але це, мабуть, буде марним, оскільки тільки центральне сховище і його лічильник має значення для всіх. === Порожні підкаталоги === Порожні підкаталоги не можуть відслідковуватися. Створюйте підставні файли, щоб обійти цю проблему. В цьому винен не дизайн Git, а його поточна реалізація. Якщо пощастить і користувачі Git будуть піднімати більше галасу навколо цієї функції, можливо вона буде реалізована. === Початковий комміт === Шаблонний комп'ютерщик рахує з 0, а не з 1. На жаль, у відношенні коммітов Git не дотримується цієї угоди. Багато команд недружелюбні до первісного комміта. Крім того, деякі окремі випадки вимагають спеціальної обробки, наприклад rebase гілки з іншим початковим коммітом. Git'у було б вигідно визначити нульовий комміт: при створенні сховища HEAD був би встановлений в рядок, що складається з 20 нульових байтів. Цей спеціальний комміт являв би собою порожнє дерево, без батьків, яке передує кожному сховищу Git. Тоді запуск *git log*, наприклад, показував би користувачеві, що комміти ще не були зроблені, замість того щоб завершуватися з фатальною помилкою. Аналогічно для інших інструментів. Кожен початковий комміт — неявний нащадок цього нульового комміта. Однак тут, на жаль, є деякі проблемні випадки. Якщо кілька гілок з різними початковими коммітами зливаються, то rebase результату вимагає значного ручного втручання. === Чудасії інтерфейсу === Для коммітів А і Б значення виразів "A..B" і "A...B" залежать від того, чи очікує команда вказівки двох кінцевих точок або проміжку. Дивіться *git help diff* та *git help rev-parse*. gitmagic-20160304/uk/branch.txt0000644000175000017500000005044412666307504015424 0ustar sbadiasbadia== Чудеса розгалуження == Можливості миттєвого розгалуження і злиття — найкращі особливості Git. *Завдання*: зовнішні фактори неминуче потребують переключення уваги. Серйозна помилка в уже випущеній версії виявляється без попередження. Термін здачі певної функціональності наближається. Розробник, допомога якого потрібна вам в роботі над ключовою частиною проекту, збирається у відпустку. Одним словом, вам потрібно терміново кинути все, над чим ви працюєте в даний момент, і переключитися на зовсім інші завдання. Переривання ходу ваших думок може серйозно знизити ефективність роботи, і чим складніше перемикання між процесами, тим більшою буде втрата. При централізованому керуванні версіями ми змушені завантажувати свіжу робочу копію з центрального сервера. Розподілена система краще: ми можемо клонувати потрібну версію локально. Проте клонування все ж передбачає копіювання всього робочого каталогу, як і всієї історії змін до теперішнього моменту. Хоча Git і знижує затратність цієї дії за рахунок можливості спільного використання файлів і жорстких посилань, але всі файли проекту доведеться повністю відтворити в новому робочому каталозі. *Розв'язання*: у Git є більш зручний інструмент для таких випадків, який заощадить і час, і дисковий простір в порівнянні з клонуванням — це *git branch* (branch — гілка, прим. пер.). Цим чарівним словом файли в вашому каталозі миттєво перетворяться від однієї версії до іншої. Ця зміна дозволяє зробити набагато більше, ніж просто повернутися назад або просунутися вперед в історії. Ваші файли можуть зміниться з останньої випущеної версії на експериментальну, з експериментальної — на поточну версію у розробці, з неї — на версію вашого друга і так далі. === Кнопка боса === Грали коли-небудь в одну з таких ігор, де при натисканні певної клавіші (``кнопки боса''), на екрані миттєво відображається таблиця або щось на зразок того? Тобто, якщо в офіс зайшов начальник, а ви граєте в гру, ви можете швидко її приховати. У якомусь каталозі: $ echo "Я хитріший за мого боса" > myfile.txt $ git init $ git add . $ git commit -m "Початковий комміт" Ми створили сховище Git, що містить один текстовий файл з певним повідомленням. Тепер виконайте $ git checkout -b boss # ймовірно, це остання зміна $ echo "Мій бос розумніший за мене" > myfile.txt $ git commit -a -m "Інший комміт" Це виглядає так, ніби ми тільки що перезаписали файл і зробили комміт. Але це ілюзія. Наберіть $ git checkout master # переключитися на оригінальну версію файлу Вуаля! Текстовий файл відновлений. А якщо бос вирішить сунути ніс в цей каталог, запустіть $ git checkout boss # перейти на версію, яка підходить для очей боса Ви можете переключатися між двома версіями цього файлу так часто, як вам хочеться і робити комміти кожної з них незалежно. === Брудна робота === [[branch]] Припустимо, ви працюєте над якоюсь функцією, і вам навіщось знадобилося повернутися на три версії назад і тимчасово додати кілька операторів виводу, щоб подивитися як щось працює. Тоді введіть $ git commit -a $ git checkout HEAD~3 Тепер ви можете додавати тимчасовий чорновий код в будь-яких місцях. Можна навіть закоммітити ці зміни. Коли закінчите, виконайте $ git checkout master щоб повернутися до вихідної роботі. Зауважте, що будь-які зміни, які не внесені в комміт, будуть перенесені. А що, якщо ви все-таки хотіли зберегти тимчасові зміни? Запросто: $ git checkout -b dirty а потім зробіть комміт перед поверненням в гілку master. Кожного разу, коли ви захочете повернутися до чорнових змін, просто виконайте $ git checkout dirty Ми говорили про цю команду в одному із попередніх розділів, коли обговорювали завантаження старих станів. Тепер у нас перед очима повна картина: файли змінилися до потрібного стану, але ми повинні залишити головну гілку. Будь-які комміти, зроблені з цього моменту, направлять файли по іншому шляху, до якого можна буде повернутися пізніше. Іншими словами, після перемикання на більш старий стан Git автоматично направляє вас по новій безіменній гілці, якій можна дати ім'я і зберегти її за допомогою *git checkout -b*. === Швидкі виправлення === Ваша робота в самому розпалі, коли раптом з'ясовується, що потрібно все кинути і виправити тільки що виявлену помилку в комміті `1b6d...`: $ git commit -a $ git checkout -b fixes 1b6d Після виправлення помилки виконайте $ git commit -a -m "Помилка виправлена" $ git checkout master і поверніться до роботи над вашими початковими завданнями. Ви можете навіть 'влити' тільки що зроблене виправлення помилки в основну гілку: $ git merge fixes === Злиття === У деяких системах керування версіями створювати гілки легко, а от зливати їх воєдино важко. У Git злиття настільки тривіальне, що ви можете його не помітити. Насправді ми стикалися зі злиттями вже давно. Команда *pull* по суті отримує комміти, а потім зливає їх з вашою поточної гілкою. Якщо у вас немає локальних змін, злиття відбудеться само собою, як вироджений випадок на кшталт отримання останньої версії в централізованій системі управління версіями. Якщо ж у вас є локальні зміни, Git автоматично зробить злиття і повідомить про будь-які конфлікти. Зазвичай у комміта є один 'батько', а саме попередній комміт. Злиття гілок призводить до комміту як мінімум з двома батьками. Звідси виникає питання: до якого комміту насправді відсилає `HEAD~10`? Комміт може мати кілька батьків, так за яким з них слідувати далі? Виявляється, такий запис завжди вибирає першого батька. Це хороший вибір, тому що поточна гілка стає першим батьком під час злиття. Часто вас цікавлять тільки зміни, зроблені вами в поточній гілці, а не ті, які влилися з інших гілок. Ви можете звертатися до конкретного батька за допомогою символу '^'. Наприклад, щоб показати запис у журналі від другого батька, наберіть $ git log HEAD^2 Для першого батька номер можна опустити. Наприклад, щоб показати різницю з першим батьком, введіть $ git diff HEAD^ Ви можете поєднувати такий запис з іншими. Наприклад, $ git checkout 1b6d^^2~10 -b ancient створить нову гілку ``ancient'', що відображає стан на десять коммітов назад від другого батька першого батька комміта, що починається з 1b6d. === Неперервний робочий процес === У виробництві техніки часто буває, що другий крок плану повинен чекати завершення першого кроку. Автомобіль, що потребує ремонту, може тихо стояти в гаражі до прибуття з заводу конкретної деталі. Прототип може чекати виробництва чіпа, перш ніж розробка буде продовжена. І в розробці ПЗ може бути те ж. Друга порція нової функціональності може бути змушена чекати випуску та тестування першої частини. Деякі проекти вимагають перевірки вашого коду перед його прийняттям, так що ви повинні дочекатися затвердження першої частини, перш ніж починати другу. Завдяки безболісним галуженню і злиттю, ми можемо змінити правила і працювати над другою частиною до того, як перша офіційно буде готова. Припустимо, ви закоммітили першу частину і вислали її на перевірку. Скажімо, ви в гілці master. Тепер змініть гілку: $ git checkout -b part2 # частина2 Потім працюйте над другою частиною, попутно вносячи комміти ваших змін. Людині властиво помилятися, і часто ви схочете повернутися і поправити щось в першій частині. Якщо ви везучі або дуже вправні, можете пропустити ці рядки. $ git checkout master # Повертаємося до першої частини. $ вносимо_зміни $ git commit -a # Фіксуємо зміни $ git checkout part2 # Повертаємося до другої частини. $ git merge master # Вливаємо зроблені виправлення. Зрештою, перша частина затверджена: $ git checkout master # Повертаємося до першої частини. $ отправка файлов # Випускаємо в світ! $ git merge part2 # Вливаємо другу частину. $ git branch -d part2 # Видаляємо гілку part2. Тепер ви знову в гілці master, а друга частина — у вашому робочому каталозі. Цей прийом легко розширити на будь-яку кількість частин. Настільки ж легко змінити гілку заднім числом. Припустимо, ви надто пізно виявили, що повинні були створити гілку сім коммітів назад. Тоді введіть: $ git branch -m master part2 # Перейменовуємо гілку master в part2. $ git branch master HEAD~7 # Створюємо нову гілку master сімома коммітами вище. Тепер гілка master містить тільки першу частину, а гілка part2 — все інше. В останній ми і знаходимося. Ми створили гілку master, не перемикаючись на неї, тому що хочемо продовжити роботу над part2. Це незвично: досі ми переключалися на гілки відразу ж після їх створення, ось так: $ git checkout HEAD~7 -b master # Створюємо гілку і переключаємося на неї. === Змінюємо склад суміші === Припустимо, вам подобається працювати над всіма аспектами проекту в одній і тій же гілці. Ви хочете закрити свій робочий процес від інших, щоб всі бачили ваші комміти тільки після того, як вони будуть добре оформлені. Створіть пару гілок: $ git branch sanitized # Створюємо гілку для очищених коммітів. $ git checkout -b medley # Створюємо гілку для роботи і переключаємося на неї. Далі робіть все що потрібно: виправляйте помилки, додавайте нові функції, додавайте тимчасовий код і так далі, при цьому частіше виконуючи комміти. Після цього $ git checkout sanitized $ git cherry-pick medley^^ застосує комміт пра-батька голови гілки ``medley'' до гілки ``sanitized''. Правильно підбираючи елементи, ви зможете створити гілку, в якій буде лише остаточний код, а пов'язані між собою комміти будуть зібрані разом. === Управління гілками === Для перегляду списку всіх гілок наберіть $ git branch За замовчуванням ви починаєте з гілки під назвою ``master''. Комусь подобається залишати гілку ``master'' недоторканою і створювати нові гілки зі своїми змінами. Опції *-d* і *-m* дозволяють видаляти і переміщати (перейменовувати) гілки. Дивіться *git help branch*. Гілка ``master'' — це зручна традиція. Інші можуть припускати, що у вашому сховищі є гілка з таким ім'ям і що вона містить офіційну версію проекту. Хоча ви можете перейменувати або знищити гілку ``master'', краще дотриматися загальної угоди. === Тимчасові гілки === Через якийсь час ви можете виявити, що створюєте безліч тимчасових гілок для однієї і тієї ж короткострокової мети: кожна така гілка лише зберігає поточний стан, щоб ви могли повернутися назад і виправити серйозну помилку або зробити щось ще. Це схоже на те, як ви перемикаєте телевізійні канали, щоб подивитися що показують по іншим. Але замість того, щоб натиснути на пару кнопок, вам потрібно створювати, вибирати (checkout), зливати (merge) а потім видаляти тимчасові гілки. На щастя, в Git є скорочена команда, настільки ж зручна, як пульт дистанційного керування. $ git stash Ця команда збереже поточний стан в у тимчасовому місці ('схованці', stash) і відновить попередній стан. Ваш каталог стає точно таким, яким був до початку редагування, і ви можете виправити помилки, завантажити віддалені зміни тощо. Коли ви хочете повернутися назад в стан схованки, наберіть: $ git stash apply # Можливо, знадобиться усунути конфлікти, що виникнули. Можна створювати кілька схованок, використовуючи їх по-різному. Дивіться *git help stash*. Як ви могли здогадатися, Git залишає гілки 'за кадром' при виконанні цього чудового прийому. === Працюйте як вам подобається === Можливо, ви сумніваєтеся, чи варті гілки таких клопотів. Зрештою, клони майже настільки ж швидкі і ви можете перемикатися між ними за допомогою *cd* замість загадкових команд Git. Поглянемо на веб-браузери. Навіщо потрібна підтримка вкладок на додаток до вікон? Підтримка і тих, і інших дозволяє пристосуватися до широкої різноманітності стилів роботи. Деяким користувачам подобається тримати відкритим єдине вікно і використовувати вкладки для безлічі веб-сторінок. Інші можуть впасти в іншу крайність: безліч вікон без вкладок взагалі. Треті віддадуть перевагу чомусь середньому. Гілки схожі на вкладки для робочого каталогу, а клони — на нові вікна браузера. Ці операції швидкі і виконуються локально, так чому б не поекспериментувати і не знайти найбільш зручну для себе комбінацію? Git дозволяє працювати в так, як вам подобається. gitmagic-20160304/uk/secrets.txt0000644000175000017500000004770012666307504015640 0ustar sbadiasbadia== Розкриваємо таємниці == Ми заглянемо під капот і пояснимо, як Git творить свої чудеса. Я опущу зайві деталі. За більш детальними описами зверніться до http://schacon.github.com/git/user-manual.html[посібник користувача]. === Невидимість === Як Git може бути таким ненав'язливим? За винятком періодичних коммітов і злиттів, ви можете працювати так, як ніби й не підозрюєте про якесь управління версіями. Так відбувається до того моменту, коли Git вам знадобиться, і тоді ви з радістю побачите, що він спостерігав за вами весь цей час. Інші системи управління версіями змушують вас постійно боротися з загородками і бюрократією. Файли можуть бути доступні тільки для читання, поки ви явно не вкажете центральному серверу, які файли ви маєте намір редагувати. Із збільшенням кількості користувачів більшість базових команд починають виконуватися все повільніше. Неполадки з мережею або з центральним сервером повністю зупиняють роботу. На противагу цьому, Git просто зберігає історію проекту в підкаталозі.git вашого робочого каталогу. Це ваша особиста копія історії, тому ви можете залишатися поза мережею, поки не захочете взаємодіяти з іншими. У вас є повний контроль над долею ваших файлів, оскільки Git в будь-який час може легко відновити збережений стан з.git. === Цілісність === Більшість людей асоціюють криптографію з утриманням інформації в таємниці, але іншим настільки ж важливим завданням є утримання її в цілості. Правильне використання криптографічних хеш-функцій може запобігти випадковому або зловмисному пошкодженню даних. SHA1 хеш можна розглядати як унікальний 160-бітний ідентифікатор для кожного рядка байт, з яким ви стикаєтеся у вашому житті. Навіть більше того: для кожного рядка байтів, який будь-яка людина коли-небудь буде використовувати протягом багатьох життів. Оскільки SHA1 хеш сам є послідовністю байтів, ми можемо отримати хеш рядка байтів, що містить інші хеши. Це просте спостереження на подив корисно: шукайте 'hash chains' (ланцюги хешів). Пізніше ми побачимо, як Git використовує їх для ефективного забезпечення цілісності даних. Кажучи коротко, Git зберігає ваші дані в підкаталозі `.git/objects`, де замість нормальних імен файлів ви знайдете тільки ідентифікатори. Завдяки використанню ідентифікаторів в якості імен файлів, а також деяких хитрощів з файлами блокувань і тимчасовими мітками, Git перетворює будь-яку скромну файлову систему в ефективну і надійну базу даних. === Інтелект === Як Git дізнається, що ви перейменували файл, навіть якщо ви ніколи не згадували про це явно? Звичайно, ви можете запустити *git mv*; але це те ж саме, що *git rm*, а потім *git add*. Git евристично знаходить файли, які були перейменовані або скопійовані між сусідніми версіями. Насправді він може виявити, що ділянки коду були переміщені або скопійовані між файлами! Хоча Git не може охопити всі випадки, він все ж робить гідну роботу, і ця функція постійно покращується. Якщо вона не спрацювала, спробуйте опції, що включають більш ресурсномістке виявлення копіювання і подумайте про оновлення. === Індексація === Для кожного відстежуваного файлу, Git записує таку інформацію, як розмір, час створення і час останньої зміни, у файлі, відомому як 'index'. Щоб визначити, чи був файл змінений, Git порівнює його поточні характеристики із збереженими в індексі. Якщо вони збігаються, то Git не стане перечитувати файл заново. Оскільки зчитування цієї інформації значно швидше, ніж читання всього файлу, то якщо ви редагували лише кілька файлів, Git може оновити свій індекс майже миттєво. Ми відзначали раніше, що індекс – це буферна зона. Чому набір властивостей файлів виступає таким буфером? Тому що команда add поміщає файли в базу даних Git і у відповідності з цим оновлює ці властивості; тоді як команда commit без опцій створює комміт, заснований тільки на цих властивостях і файлах, які вже в базі даних. === Походження Git === Це http://lkml.org/lkml/2005/4/6/121[повідомлення в поштовій розсилці ядра Linux] описує послідовність подій, які призвели до появи Git. Весь цей тред – приваблива археологічна розкопка для істориків Git. === База даних об'єктів === Кожна версія ваших даних зберігається в «базі даних об’єктів», що живе в підкаталозі `.git/objects`. Інші „жителі“ `.git/` містять вторинні дані: індекс, імена гілок, теги, параметри налаштування, журнали, нинішнє розташування „головного“ комміта і так далі. База об’єктів проста і елегантна, і в ній джерело сили Git. Кожен файл всередині `.git/objects` — це „об’єкт“. Нас цікавлять три типи об’єктів: об’єкти „блоб“, об’єкти „дерева“ і об’єкти „комміти“. === Блоби === Для початку один фокус. Виберіть ім'я файлу – будь-яке ім'я файлу. У порожньому каталозі: $ echo sweet > ВАШЕ_ІМ’Я_ФАЙЛУ $ git init $ git add . $ find .git/objects -type f Ви побачите +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. Звідки я знаю це, не знаючи імені файлу? Це тому, що SHA1 хеш рядка "blob" SP "6" NUL "sweet" LF дорівнює aa823728ea7d592acc69b36875a482cdf3fd5c8d, де SP – це пробіл, NUL — нульовий байт і LF — переведення строки. Ви можете перевірити це, набравши $ printf "blob 6\000sweet\n" | sha1sum Git використовує „адресацію по вмісту“: файли зберігаються у відповідності не з іменами, а з хешамі вмісту, — у файлі, який ми називаємо „блоб-об’єктом“. Хеш можна розуміти як унікальний ідентифікатор вмісту файлу, що означає звернення до файлів за їх вмістом. Початковий `blob 6` — лише заголовок, що складається з типу об'єкта і його довжини в байтах і спрощує внутрішній облік. Таким чином, я можу легко передбачити, що ви побачите. Ім'я файлу не має значення: для створення блоб-об’єкта використовується тільки його вміст. Вам може бути цікаво, що відбувається з однаковими файлами. Спробуйте додати копії свого файлу з якими завгодно іменами. Зміст +.git/objects+ залишиться тим же незалежно від того, скільки копій ви додасте. Git зберігає дані лише одного разу. Доречі, файли в каталозі +.git/objects+ стискаються за допомогою zlib тому ви не зможете переглянути їх безпосередньо. Пропустіть їх через фільтр http://www.zlib.net/zpipe.c[zpipe -d], або наберіть $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d що виведе зазначений об’єкт у вигляді, придатному для читання. === Дерева === Але де ж імена файлів? Вони повинні зберігатися на якомусь рівні. Git звертається за іменами під час комміта: $ git commit # Введіть який-небудь опис $ find .git/objects -type f Тепер ви повинні побачити три об'єкти. На цей раз я не можу сказати вам, що з себе представляють два нових файли, оскільки це частково залежить від обраного вами імені файлу. Далі будемо припускати, що ви назвали його ``rose''. Якщо це не так, то ви можете переписати історію, щоб вона виглядала як ніби ви це зробили: $ git filter-branch --tree-filter 'mv ВАШЕ_ІМ’Я_ФАЙЛУ rose' $ find .git/objects -type f Тепер ви повинні побачити файл +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, оскільки це SHA1 хеш його вмісту: «tree» SP «32» NUL «100644 rose» NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Перевірте, що цей файл дійсно містить зазначений рядок, набравши $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch З zpipe легко перевірити хеш: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum Перевірка хешу за допомогою cat-file складніша, оскільки її висновок містить не тільки „сирий“ розпакований файл об’єкта. Цей файл — об'єкт „дерево“ ('tree', прим. пер.): перелік ланцюгів, що складаються з типу, імені файлу та його хешу. У нашому прикладі: тип файлу — 100644, що означає, що `rose` це звичайний файл; а хеш — блоб-об’єкт, в якому міститься вміст `rose`. Інші можливі типи файлів: виконувані файли, символічні посилання або каталоги. В останньому випадку, хеш вказує на об’єкт „дерево“. Якщо ви запускали filter-branch, у вас є старі об’єкти які вам більше не потрібні. Хоча після закінчення терміну зберігання вони будуть викинуті автоматично, ми видалимо їх зараз, щоб було легше стежити за нашим іграшковим прикладом: $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune Для реальних проектів зазвичай краще уникати таких команд, оскільки ви знищуєте резервні копії. Якщо ви хочете мати чисте сховище, то звичайно краще зробити свіжий клон. Крім того, будьте обережні при безпосередньому втручанні в каталог +.git+: що якщо інша команда Git працює в цей же час, або раптово збій в електропостачанні? Взагалі кажучи, посилання потрібно видаляти за допомогою *git update-ref -d*, хоча зазвичай ручне видалення +refs/original+ безпечне. === Комміти === Ми розглянули два з трьох об'єктів. Третій об’єкт — „комміт“ (commit). Його вміст залежить від опису комміта, як і від дати і часу його створення. Для відповідповідності тому, що ми маємо, ми повинні трохи „підкрутити“ Git: $ git commit --amend -m Shakespeare # Змінимо опис комміта. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Підробимо тимчасові мітки і авторів. $ find .git/objects -type f Тепер ви повинні побачити +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ який є SHA1 хешем його вмісту: «commit 158» NUL «tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9» LF «author Alice 1234567890 -0800» LF «committer Bob 1234567890 -0800» LF LF «Shakespeare» LF Як і раніше, ви самі можете запустити zpipe або cat-file, щоб побачити це. Це перший комміт, тому тут немає батьківських коммітів, але подальші комміти завжди будуть містити хоча б один рядок, що ідентифікує батьківський комміт. === Невідрізнено від чаклунства === Секрети Git виглядають занадто простими. Схоже, що ви могли б об’єднати кілька shell-скриптів і додати трохи коду на C, щоб зробити все це в лічені години: суміш базових операцій з файлами і SHA1-хешування, приправлена ​​блокувальними файлами і fsync для надійності. По суті, це точний опис ранніх версій Git. Тим не менше, крім геніальних трюків з упаковкою для економії місця і з індексацією для економії часу, ми тепер знаємо, як спритно Git перетворює файлову систему в базу даних, що ідеально підходить для керування версіями. Наприклад, якщо який-небудь файл у базі даних об’єктів пошкоджений через помилку диска, то його хеш тепер не співпаде, що приверне нашу увагу до проблеми. За допомогою хешування хешів інших об’єктів, ми підтримуємо цілісність на всіх рівнях. Комміти атомарні, так що в них ніколи не можна записати лише частину змін: ми можемо обчислити хеш комміта і зберегти його в базу даних тільки зберігши всі відповідні дерева, блоби і батьківські комміти. База даних об’єктів нечутлива до непередбачених переривань роботи, таких як перебої з живленням. Ми завдаємо поразки навіть найбільш хитрим супротивникам. Припустимо, хтось намагається таємно змінити вміст файлу в давньої версії проекту. Щоб база об’єктів виглядала неушкодженою, він також повинен змінити хеш відповідного блоб-об'єкта, оскільки це тепер інша послідовність байтів. Це означає, що потрібно поміняти хеши всіх об’єктів дерев, що посилаються на цей файл; що в свою чергу змінить хеши всіх об’єктів коммітів за участю таких дерев; а також і хеши всіх нащадків цих коммітів. Внаслідок цього хеш офіційної головної ревізії буде відрізнятися від аналогічного хешу в цьому зіпсованому сховищі. По ланцюжку незбіжних хешів ми можемо точно обчислити спотворений файл, як і комміт, де він спочатку був пошкоджений. Одним словом, неможливо підробити сховище Git, залишивши непошкодженими двадцять байт, що відповідають останньому комміту. Як щодо відомих характерних особливостей Git? Галуження? Злиття? Теги? Очевидні подробиці. Поточна «голова» зберігається у файлі +.git/HEAD+, що містить хеш об’єкта комміта. Хеш оновлюється під час комміта, а також при виконанні багатьох інших команд. З гілками все аналогічно: це файли в +.git/refs/heads+. Те ж і з тегами: вони живуть у +.git/refs/tags+, але їх оновлює інший набір команд. gitmagic-20160304/uk/multiplayer.txt0000644000175000017500000003601512666307504016534 0ustar sbadiasbadia== Багатокористувацький Git == Спочатку я використовував Git для особистого проекту, в якому був єдиним розробником. Серед команд, які відносяться до розподілених властивостей Git, мені були потрібні тільки *pull* і *clone*, щоб зберігати один і той же проект у різних місцях. Пізніше я захотів опублікувати свій код за допомогою Git і включати зміни помічників. Мені довелося навчитися керувати проектами, в яких беруть участь багато людей по всьому світу. На щастя, в цьому сильна сторона Git і, можливо, сам сенс його існування. === Хто я? === Кожен комміт містить ім'я автора та адресу електронної пошти, які виводяться командою *git log*. За замовчуванням Git використовує системні налаштування для заповнення цих полів. Щоб встановити їх явно, введіть $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com Щоб встановити ці параметри лише для поточного сховища, пропустіть опцію --global. === Git через SSH, HTTP === Припустимо, у вас є SSH доступ до веб-сервера, але Git не встановлений. Git може зв'язуватися через HTTP, хоча це і менш ефективно, ніж його власний протокол. Скачайте, скомпілюйте, встановіть Git у вашому акаунті; створіть сховище в каталозі, доступному через web: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update Для старих версій Git команда копіювання не спрацює і ви повинні будете запустити $ chmod a+x hooks/post-update Тепер ви можете публікувати свої останні правки через SSH з будь-якого клону: $ git push веб.сервер:/шлях/до/proj.git master і хто завгодно зможе взяти ваш проект за допомогою $ git clone http://веб.сервер/proj.git === Git через що завгодно === Хочете синхронізувати сховища без серверів або взагалі без мережевого підключення? Змушені імпровізувати на ходу в непередбаченій ситуації? Ми бачили, як <>. За допомогою обміну такими файлами ми можемо переносити сховища git будь-якими доступними засобами, але є більш ефективний інструмент: *git bundle*. Відправник створює пакет (bundle): $ git bundle create деякий_файл HEAD Потім передає пакет, +деякий_файл+, іншій команді будь-якими засобами, такими як: електронна пошта, флешка, *xxd* друк і подальше розпізнавання тексту, надиктовка бітів по телефону, димові сигнали і так далі. Одержувач відновлює комміти з пакету, ввівши $ git pull деякий_файл Одержувач може зробити це навіть у порожньому сховищі. Незважаючи на свій невеликий розмір, +деякий_файл+ містить все вихідне сховище Git. У великих проектах для усунення надлишків обсягу пакетують тільки зміни, яких немає в інших сховищах. Наприклад, нехай комміт ``1b6d...'' — останній спільний для обох груп: $ git bundle create деякий_файл HEAD ^1b6d Якщо це робиться часто, можна легко забути, який комміт був відправлений останнім. Довідка пропонує для вирішення цієї проблеми використовувати теги. А саме, після передачі пакета введіть $ git tag -f останній_пакет HEAD і створюйте оновлені пакети за допомогою $ git bundle create новий_пакет HEAD ^останній_пакет === Патчі: загальне застосування === Патчі — це тексти змін, цілком зрозумілі як для людини, так і комп’ютера. Це робить їх дуже привабливим форматом обміну. Патч можна послати розробникам по електронній пошті, незалежно від того, яку систему управління версіями вони використовують. Вашим кореспондентам достатньо можливості читати електронну пошту, щоб побачити ваші зміни. Точно так само, з Вашого боку потрібна лише адреса електронної пошти: немає потреби в налаштуванні онлайн сховища Git. Пригадаємо з першого розділу: $ git diff 1b6d виводить патч, який може бути вставлений в лист для обговорення. У Git сховищі введіть $ git apply < мій.patch для застосування патча. У більш формальних випадках, коли потрібно зберегти ім’я автора та підписи, створюйте відповідні патчі з заданої точки, набравши $ git format-patch 1b6d Отримані файли можуть бути відправлені за допомогою *git-send-email* або вручну. Ви також можете вказати діапазон коммітів: $ git format-patch 1b6d..HEAD^^ На приймаючій стороні збережіть лист в файл і введіть: $ git am < email.txt Це застосує вхідні виправлення і створить комміт, що включає ім’я автора та іншу інформацію. З web-інтерфейсом до електронної пошти вам, можливо, буде потрібно натиснути кнопку, щоб подивитися електронну пошту в своєму початковому вигляді перед збереженням патча в файл. Для клієнтів електронної пошти, що використовують mbox, є невеликі відмінності, але якщо ви використовуєте один з них, то ви, очевидно, можете легко розібратися в цьому без читання описів! === Приносимо вибачення, ми переїхали === Після клонування сховища команди *git push* або *git pull* автоматично відправляють і отримують його за початковою адресою. Яким чином Git це робить? Секрет полягає в налаштуваннях, заданих при створенні клона. Давайте поглянемо: $ git config --list Опція +remote.origin.url+ задає вихідний адресу; ``origin'' — ім'я початкового сховища. Як і ім'я гілки ``master'', це домовленість. Ми можемо змінити або видалити це скорочене ім'я, але як правило, немає причин для цього. Якщо оригінальне сховище переїхало, можна оновити його адресу командою $ git config remote.origin.url git://новий.url/proj.git Опція +branch.master.merge+ задає віддалену гілку за замовчуванням для *git pull*. В ході початкового клонування вона встановлюється на поточну гілку джерельного сховища, так що навіть якщо HEAD джерельного сховища згодом переміститься на іншу гілку, pull буде вірно слідувати початковій гілці. Цей параметр звертається тільки до сховища, яке ми спочатку клонували і яке записано в параметрі +branch.master.remote+. При виконанні pull з інших сховищ ми повинні вказати потрібну гілку: $ git pull git://приклад.com/other.git master Це пояснює, чому деякі з наших попередніх прикладів push і pull не мали аргументів. === Віддалені гілки === При клонуванні сховища ви також клонуєте всі його гілки. Ви можете не помітити цього, тому що Git приховує їх: ви повинні запитати їх явно. Це запобігає протиріччю між гілками у віддаленому сховищі і вашими гілками, а також робить Git простішим для початківців. Список віддалених гілок можна подивитися командою $ git branch -r Ви повинні побачити щось подібне origin/HEAD origin/master origin/experimental Ці імена відповідають гілкам і „голові“ у віддаленому сховищі; їх можна використовувати в звичайних командах Git. Наприклад, ви зробили багато коммітів, і хотіли б порівняти поточний стан з останньою завантаженою версією. Ви можете шукати в журналах потрібний SHA1 хеш, але набагато легше набрати $ git diff origin/HEAD Також можна побачити, для чого була створена гілка ``experimental'': $ git log origin/experimental === Кілька віддалених сховищ === Припустимо, що над нашим проектом працюють ще два розробники і ми хочемо стежити за обома. Ми можемо спостерігати більш ніж за одним сховищем одночасно, ось так: $ git remote add other git://приклад.com/деяке_сховище.git $ git pull other деяка_гілка Зараз ми зробили злиття з гілкою з другого сховища. Тепер у нас є легкий доступ до всіх гілок у всіх сховищах: $ git diff origin/experimental^ other/деяка_гілка~5 Але що якщо ми просто хочемо порівняти їх зміни, не зачіпаючи свою роботу? Іншими словами, ми хочемо вивчити чужі гілки, не даючи їх змінам вторгатися в наш робочий каталог. Тоді замість pull наберіть $ git fetch # Перенести із origin, типово. $ git fetch other # Перенести від другого програміста. Так ми лише переносимо їх історію. Хоча робочий каталог залишається недоторканими, ми можемо звернутися до будь-якої гілки в будь-якому сховищі команди, працюючої з Git, оскільки тепер у нас є локальна копія. Пригадаємо, що pull це просто *fetch*, а потім *merge*. Зазвичай ми використовуємо *pull*, тому що ми хочемо влити до себе останній комміт після отримання чужої гілки. Описана ситуація — помітний виняток. Про те, як відключити віддалені сховища, ігнорувати окремі гілки і багато іншого дивіться у *git help remote*. === Мої вподобання === Я віддаю перевагу тому, щоб люди, що приєднуються до моїх проектів, створювали сховища, з яких я зможу отримувати зміни за допомогою pull. Деякі хостинги Git дозволяють створювати власні розгалуження (форки) проекту в один дотик. Після отримання дерева з віддаленого сховища я запускаю команди Git для навігації і вивчення змін, в ідеалі добре організованих і описаних. Я роблю злиття зі своїми змінами і можливо вношу подальші правки. Коли я задоволений результатом, я заливаю зміни в головне сховище. Хоча зі мною мало співпрацюють, я вірю, що цей підхід добре масштабується. Дивіться http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[цей запис в блозі Лінуса Торвальдса]. Залишатися в світі Git трохи зручніше, ніж використовувати файли патчів, оскільки це позбавляє мене від перетворення їх в комміти Git. Крім того, Git керує деталями на зразок збереження імені автора та адреси електронної пошти, а також дати і часу, і просить авторів описувати свої зміни. gitmagic-20160304/uk/preface.txt0000644000175000017500000001321012666307504015562 0ustar sbadiasbadia= Магія Git = Ben Lynn Серпень 2007 == Передмова == http://git.or.cz/[Git] — це швейцарський ніж керування версіями — надійний універсальний багатоцільовий інструмент, чия надзвичайна гнучкість робить його складним у вивченні навіть для багатьох професіоналів. Як говорив Артур Кларк, будь-яка досить розвинена технологія не відрізняється від чаклунства. Це відмінний підхід до Git: новачки можуть ігнорувати принципи його внутрішньої роботи і розглядати Git як щось, що викликає захоплення у друзів і доводить до сказу ворогів своїми чудовими здібностями. Замість того, щоб вдаватися в подробиці, ми дамо приблизні інструкції для одержання конкретних результатів. При частому використанні ви поступово зрозумієте, як працює кожен трюк і як пристосовувати рецепти під ваші потреби. .Переклади - link:/~blynn/gitmagic/intl/vi/[В'єтнамська]: Trần Ngọc Quân; також http://vnwildman.users.sourceforge.net/gitmagic/[розміщено на його вебсайті]. - link:/~blynn/gitmagic/intl/es/[Іспанська]: Rodrigo Toledo та Ariset Llerena Tapia. - http://docs.google.com/View?id=dfwthj68_675gz3bw8kj[Китайська (спрощена)]: JunJie, Meng та JiangWei. Конвертовано у link:/~blynn/gitmagic/intl/zh_tw/[Традиційна китайська] via +cconv -f UTF8-CN -t UTF8-TW+. - link:/~blynn/gitmagic/intl/de/[Німецька]: Benjamin Bellee і Armin Stebich. Armin також розмістив http://gitmagic.lordofbikes.de/[німецький переклад на своєму сайті]. - http://www.slideshare.net/slide_user/magia-git[Португальська]: Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[в форматі ODT]]. - link:/~blynn/gitmagic/intl/ru/[Російська]: Тихон Тарнавский, Михаил Дымсков і інші. - link:/~blynn/gitmagic/intl/uk/[Українська]: Володимир Боденчук. - link:/~blynn/gitmagic/intl/fr/[Французька]: Alexandre Garel, Paul Gaborit, та Nicolas Deram. Також розміщений на http://tutoriels.itaapy.com/[itaapy]. .Інші варіанти - link:book.html[HTML однією сторінкою]: чистий HTML без CSS. - link:book.pdf[PDF файл]: для друку. - http://packages.debian.org/gitmagic[пакунок Debian], http://packages.ubuntu.com/gitmagic[пакунок Ubuntu]: отримайте локальну копію цього сайту. Стане у нагоді, http://csdcf.stanford.edu/status/[якщо цей сервер буде недоступним]. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[друкована версія [Amazon.com]]: 64 сторінок, 15.24cm x 22.86cm, чорно-біле зображення. Стане у нагоді у випадку відсутності електроенергії. === Подяки === Я дуже ціную, що так багато людей працювали над перекладами цих рядків. Я вдячний названим вище людям за їхні зусилля, які розширили мою аудиторію. Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, Tyler Breisacher, Sonia Hamilton, Julian Haagsma, Romain Lespinasse, Sergey Litvinov, Oliver Ferrigni, David Toca, Сергей Сергеев, Joël Thieffry та Baiju Muthukadan сприяли в правках і доробках. François Marier супроводжує пакунок Debian, спочатку створений Daniel Baumann. Мої подяки іншим за вашу підтримку і похвалу. Мені дуже хотілося процитувати вас тут, але це могло б підняти ваше марнославство до неймовірних висот. Якщо я випадково забув згадати вас, будь ласка, нагадайте мені або просто вишліть патч. === Ліцензія === Це керівництво випущено під http://www.gnu.org/licenses/gpl-3.0.html[GNU General Public License 3-ї версії]. Природньо, вихідний текст знаходиться в сховищі Git і може бути отриманий за допомогою команди: $ git clone git://repo.or.cz/gitmagic.git # Створить каталог "gitmagic". або з одного із дзеркал: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git $ git clone https://code.google.com/p/gitmagic/ $ git clone git://git.assembla.com/gitmagic.git $ git clone git@bitbucket.org:blynn/gitmagic.git GitHub, Assembla, і Bitbucket підтримують приватні сховища, останні два безкоштовно. gitmagic-20160304/uk/basic.txt0000644000175000017500000003052212666307504015243 0ustar sbadiasbadia== Базові операції == Перш ніж занурюватися в нетрі численних команд Git, спробуйте скористатися наведеними нижче простими прикладами, щоб трохи освоїтися. Кожен із них корисний, незважаючи на свою простоту. Насправді перші місяці використання Git я не виходив за рамки матеріалу цього розділу. === Збереження стану === Збираєтеся спробувати внести якісь радикальні зміни? Попередньо створіть знімок всіх файлів у поточному каталозі за допомогою команд $ git init $ git add . $ git commit -m "Моя перша резервна копія" Тепер, якщо нові правки все зіпсували, можна відновити початкову версію: $ git reset --hard Щоб зберегти стан знову: $ git commit -a -m "Друга резервна копія" === Додавання, видалення, перейменування === Наведений вище приклад відстежує лише ті файли, які існували при першому запуску *git add*. Якщо ви створили нові файли або підкаталоги, доведеться сказати Git'у: $ git add readme.txt Documentation Аналогічно, якщо хочете, щоб Git забув про деякі файли: $ git rm kludge.h obsolete.c $ git rm -r incriminating/evidence/ Git видалить ці файли, якщо ви не видалили їх самі. Перейменування файлу — це те ж саме, що й видалення старого імені та додавання нового. Для цього є *git mv*, яка має той же синтаксис, що і команда *mv*. Наприклад: $ git mv bug.c feature.c === Розширені скасування/повернення === Іноді просто хочеться повернутися назад і забути всі зміни до певного моменту, тому що всі вони були неправильними. У такому випадку $ git log покаже список останніх коммітів і їхні хеші SHA1: ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Іван Date: Tue Mar 14 01:59:26 2000 -0800 Замінив printf() на write(). commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Марічка Date: Thu Jan 1 00:00:00 1970 +0000 Початковий комміт. ---------------------------------- Щоб вказати комміт, достатньо перших декількох символів його хешу, але можете скопіювати і весь хеш. Наберіть: $ git reset --hard 766f для відновлення стану до зазначеного комміта і видалення всіх наступних безповоротно. Можливо, іншим разом ви захочете швидко перескочити до старого стану. У цьому випадку наберіть $ git checkout 82f5 Ця команда перенесе вас назад у часі, зберігши при цьому більш нові комміти. Однак, як і у фантастичних фільмах про подорожі в часі, якщо тепер ви відредагуєте і закоммітите код, то потрапите в альтернативну реальність, тому що ваші дії відрізняються від тих, що були минулого разу. Ця альтернативна реальність називається «гілкою» (branch) і <>. А зараз просто запам'ятайте, що команда $ git checkout master поверне вас назад у теперішнє. Крім того, щоб не отримувати попереджень від Git, завжди робіть commit або скидайте зміни перед запуском checkout. Ще раз скористаємося аналогією з комп'ютерними іграми: - *git reset --hard*: завантажує раніше збережену гру і видаляє всі версії, збережені після тількищо завантаженої. - *git checkout*: завантажує стару гру, але якщо ви продовжуєте грати, стан гри буде відрізнятися від більш нових збережень, які ви зробили в перший раз. Будь-яка гра, яку ви тепер зберігаєте, потрапляє в окрему гілку, що представляє альтернативну реальність, в яку ви потрапили. <>. Можна також відновити тільки певні файли і підкаталоги, перерахувавши їх імена після команди: $ git checkout 82f5 якийсь.файл інший.файл Будьте уважні: така форма *checkout* може мовчки перезаписати файли. Щоб уникнути неприємних несподіванок, виконуйте commit перед checkout, особливо якщо ви тільки вивчаєте Git. Взагалі, якщо ви не впевнені у якісь операції, чи то команда Git чи ні, виконайте попередньо *git commit -a*. Не любите копіювати і вставляти хеші? Використовуйте $ git checkout :/"Моя перша р" для переходу на комміт, опис якого починається з наведеного рядка. Можна також запитати 5-й з кінця збережений стан: $ git checkout master~5 === Повернення === У залі суду пункти протоколу можуть викреслювати прямо під час слухання. Подібним чином і ви можете вибирати комміти для скасування. $ git commit -a $ git revert 1b6d скасує комміт із заданим хешем. Повернення буде збережене у вигляді нового комміта. Можете запустити *git log*, щоб переконатися в цьому. === Створення списку змін === Деяким проектам потрібен http://en.wikipedia.org/wiki/Changelog[спискок змін (changelog)]. Створіть його такою командою: $ git log > ChangeLog === Завантаження файлів === Отримати копію проекту під управлінням Git можна, набравши $ git clone git://сервер/шлях/до/файлів Наприклад, щоб отримати всі файли, які я використав для створення цього документу, $ git clone git://git.or.cz/gitmagic.git Пізніше ми поговоримо про команду *clone* докладніше. === Тримаючи руку на пульсі === Якщо ви вже завантажили копію проекту за допомогою *git clone*, можете оновити її до останньої версії, використовуючи $ git pull === Невідкладна публікація === Припустимо, ви написали скрипт, яким хочете поділитися з іншими. Можна просто запропонувати їм скачувати його з вашого комп'ютера, але якщо вони будуть робити це коли ви допрацьовуєте його або додаєте експериментальну функціональність, у них можуть виникнути проблеми. Очевидно, тому й існують цикли розробки. Розробники можуть постійно працювати над проектом, але загальнодоступним вони роблять свій код лише після того, як приведуть його у пристойний вигляд. Щоб зробити це за допомогою Git, виконайте в каталозі, де лежить ваш скрипт, $ git init $ git add . $ git commit -m "Перший реліз" Потім скажіть вашим користувачам запустити $ git clone ваш.комп’ютер:/шлях/до/скрипту щоб завантажити ваш скрипт. Тут мається на увазі, що у них є доступ по ssh. Якщо ні, запустіть *git daemon* і скажіть користувачам запустити цю команду замість вищенаведеної: $ git clone git://ваш.комп’ютер/шлях/до/скрипту З цих пір щоразу, коли ваш скрипт готовий до релізу, виконуйте $ git commit -a -m "Наступний реліз" і ваші користувачі зможуть оновити свої версії, перейшовши в каталог з вашим скриптом і набравши $ git pull Ваші користувачі ніколи не наткнуться на версію скрипта, яку ви не хочете їм показувати. === Що я зробив? === З'ясуйте, які зміни ви зробили з часу останнього комміта: $ git diff Чи з вчорашнього дня: $ git diff "@{yesterday}" Чи між певною версією і версією, зробленою 2 комміти назад: $ git diff 1b6d "master~2" У кожному разі на виході буде патч, який може бути застосований за допомогою *git apply*. Спробуйте також: $ git whatchanged --since="2 weeks ago" Часто замість цього я використовую для перегляду історії http://sourceforge.net/projects/qgit[qgit], через приємний інтерфейс, або http://jonas.nitro.dk/tig[tig] з текстовим інтерфейсом, який добре працює через повільне з'єднання. Як варіант, встановіть веб-сервер, введіть *git instaweb* і запустіть будь-який веб-браузер. === Вправа === Нехай A, B, C, D — чотири послідовні комміти, де В відрізняється від A лише кількома видаленими файлами. Ми хочемо повернути ці файли в D. Як ми можемо це зробити? Існує як мінімум три розв’язки. Припустимо, що ми знаходимося на D. 1. Різниця між A і B — видалені файли. Ми можемо створити патч, що відображає ці зміни, і застосувати його: $ git diff B A | git apply 2. Оскільки в комміті A ми зберегли файли, то можемо відновити їх: $ git checkout A foo.c bar.h 3. Ми можемо розглядати перехід від A до B як зміни, які хочемо скасувати: $ git revert B Який спосіб найкращий? Той, який вам більше подобається. За допомогою Git легко отримати бажане і часто існує багато способів це зробити. gitmagic-20160304/uk/grandmaster.txt0000644000175000017500000004174112666307504016476 0ustar sbadiasbadia== Гросмейстерство Git == Тепер ви вже повинні вміти орієнтуватися в сторінках *git help* і розуміти майже все. Однак точний вибір команди, необхідної для вирішення конкретної проблеми, може бути виснажливим. Можливо, я збережу вам трохи часу: нижче наведені рецепти, які знадобилися мені в минулому. === Релізи вихідних кодів === У моїх проектах Git управляє в точності тими файлами, які я збираюся архівувати і пускати в реліз. Щоб створити тарбол з вихідними кодами, я виконую: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === Комміт змін === У деяких проектах може бути трудомістким повідомляти Git про кожне додавання, видаленні та перейменування файлу. Замість цього ви можете виконати команди $ git add . $ git add -u Git прогляне файли в поточному каталозі і сам подбає про деталі. Замість другої команди add, виконайте `git commit -a`, якщо ви збираєтеся відразу зробити комміт. Дивіться * git help ignore *, щоб дізнатися як вказати файли, які повинні ігноруватися. Ви можете виконати все це одним махом: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove Опції *-z* і *-0* запобігають невірну обробку файлових імен, що містять спеціальні символи. Оскільки ця команда додає ігноровані файли, ви можливо захочете використовувати опції `-x` або `-X`. === Мій комміт занадто великий === Ви нехтували коммітамі занадто довго? Затято писали код і згадали про управління вихідними кодами тільки зараз? Внесли ряд незв'язаних змін, тому що це ваш стиль? Немає причин для занепокоєння. Виконайте $ git add -p Для кожної зробленої вами правки Git покаже змінену ділянку коду і запитає, чи повинна ця зміна потрапити в наступний комміт. Відповідайте "y" (так) або "n" (ні). У вас є й інші варіанти, наприклад відкласти вибір; введіть "?" Щоб дізнатися більше. Коли закінчите, виконайте $ git commit для внесення саме тих правок, що ви вибрали ('буферизованих' змін). Переконайтеся, що ви не вказали опцію *-a*, інакше Git закоммітить всі правки. Що робити, якщо ви змінили безліч файлів в багатьох місцях? Перевірка кожної окремої зміни стає обтяжливою рутиною. У цьому випадку використовуйте *git add -i*. Її інтерфейс не такий простий, але більш гнучкий. У декілька натискань можна додати або прибрати з буфера кілька файлів одночасно, або переглянути і вибрати зміни лише в окремих файлах. Як варіант, запустіть *git commit \--interactive*, яка автоматично зробить комміт коли ви закінчите. === Індекс — буферна зона Git === До цих пір ми уникали знаменитого 'індексу' Git, але тепер ми повинні розглянути його, для пояснення вищесказаного. Індекс це тимчасовий буфер. Git рідко переміщує дані безпосередньо між вашим проектом і його історією. Замість цього Git спочатку записує дані в індекс, а вже потім копіює їх з індексу за місцем призначення. Наприклад, *commit -a* насправді двоетапний процес. Спочатку зліпок поточного стану кожного з відстежуваних файлів поміщається в індекс. Потім зліпок, що знаходиться в індексі, записується в історію. Комміт без опції *-a* виконує тільки другий крок, і має сенс тільки після виконання команд, що змінюють індекс, таких як *git add*. Зазвичай ми можемо не звертати уваги на індекс і робити вигляд, що взаємодіємо безпосередньо з історією. Але в даному випадку ми хочемо більш тонкого контролю, тому управляємо індексом. Ми поміщаємо зліпок деяких (але не всіх) наших змін в індекс, після чого остаточно записуємо цей акуратно сформований зліпок. === Не втрачай "голови" === Тег HEAD подібний курсору, який зазвичай вказує на останній комміт, просуваючись з кожним новим коммітом. Деякі команди Git дозволяють переміщати цей курсор. Наприклад, $ git reset HEAD~3 перемістить HEAD на три комміти назад. Тепер всі команди Git будуть працювати так, ніби ви не робили останніх трьох коммітів, хоча файли залишаться в поточному стані. У довідці описано кілька способів використання цього прийому. Але як повернутися назад у майбутнє? Адже попередні комміти про нього нічого не знають. Якщо у вас є SHA1 вихідної "голови", то: $ git reset 1b6d Але припустимо, ви його не записували. Не турбуйтеся: для комнад такого роду Git зберігає оригінальну "голову" як тег під назвою ORIG_HEAD, і ви можете повернутися надійно і безпечно: $ git reset ORIG_HEAD === Полювання за "головами" === Припустимо ORIG_HEAD недостатньо. Приміром, ви тільки що усвідомили, що допустили величезну помилку, і вам потрібно повернутися до давнього комміту в давно забутій гілці. За замовчуванням Git зберігає комміти не менше двох тижнів, навіть якщо ви наказали знищити гілку, що їх містить. Проблема в знаходженні відповідного хешу. Ви можете проглянути всі значення хешів в `.git/objects` і методом проб та помилок знайти потрібний. Але є шлях значно легший. Git записує кожен підрахований ним хеш комміта в `.git/logs`. У підкаталозі `refs` міститься повна історія активності на всіх гілках, а файл `HEAD` містить кожне значення хешу, яке коли-небудь приймав HEAD. Останній можна використовувати щоб ​​знайти хеши коммітів на випадково обрубаних гілках. Команда reflog надає зручний інтерфейс роботи з цими журналами. Використовуйте $ git reflog Замість копіювання хешів з reflog, спробуйте $ git checkout "@{10 minutes ago}" Чи зробіть чекаут п'ятого з кінця з відвіданих коммітів за допомогою $ git checkout "@{5}" Дивіться розділ ``Specifying Revisions'' в *git help rev-parse* для додаткової інформації. Ви можете захотіти подовжити відстрочку для коммітів, приречених на видалення. Наприклад, $ git config gc.pruneexpire "30 days" означає, що комміти, які видаляються, будуть остаточно зникати тільки після 30 днів і після запуску *git gc*. Також ви можете захотіти відключити автоматичний виклик *git gc*: $ git config gc.auto 0 У цьому випадку комміти будуть видалятися тільки коли ви будете запускати *git gc* вручну. === Git як основа === Дизайн Git, в істинному дусі UNIX, дозволяє легко використовувати його як низькорівневий компонент інших програм: графічних та веб-інтерфейсів; альтернативних інтерфейсів командного рядка; інструментів управління патчами; засобів імпорту або конвертації, і так далі. Багато команд Git насправді - скрипти, які стоять на плечах гігантів. Невеликим доопрацюванням ви можете переробити Git на свій смак. Найпростіший трюк — використання аліасів Git для скорочення часто використовуваних команд: $ git config --global alias.co checkout $ git config --global --get-regexp alias # відображає поточні аліаси alias.co checkout $ git co foo # те ж саме, що і 'git checkout foo' Інший приклад: можна виводити поточну гілку в запрошенні командного рядка або заголовку вікна терміналу. Запуск $ git symbolic-ref HEAD виводить назву поточної гілки. На практиці ви швидше за все захочете прибрати "refs/heads/" і повідомлення про помилки: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- Підкаталог +contrib+ — це ціла скарбниця інструментів, побудованих на Git. З часом деякі з них можуть ставати офіційними командами. В Debian та Ubuntu цей каталог знаходиться у +/usr/share/doc/git-core/contrib+. Один популярний інструмент з цього каталогу — +workdir/git-new-workdir+. Цей скрипт створює за допомогою символічних посилань новий робочий каталог, який має спільну історію з оригінальним сховищем: $ git-new-workdir існуюче/сховище новий/каталог Новий каталог і файли в ньому можна сприймати як клон, з тією різницею, що два дерева автоматично залишаються синхронізованими зважаючи на спільну історію. Немає необхідності в merge, push і pull. === Ризиковані трюки === Нинішній Git робить випадкове знищення даних дуже складним. Але якщо ви знаєте, що робите, ви можете обійти захист для розповсюджених команд. *Checkout*: Наявність незакомміченних змін перериває виконання checkout. Щоб перейти до потрібного комміту, навіть знищивши свої зміни, використовуйте прапор змушування (force) *-f*: $ git checkout -f HEAD^ З іншої сторони, якщо ви вкажете checkout конкретні шляхи, перевірки на безпеку не буде: вказані файли мовчки перезапишуть. Будьте обережні при такому використанні checkout. *Reset*: скидання також переривається при наявності незакомміченних змін. Щоб змусити його спрацювати, запустіть $ git reset --hard 1b6d *Branch*: Видалення гілки припиниться, якщо воно призвело б до втрати змін. Для примусового видалення введіть $ git branch -D мертва_гілка # замість -d Аналогічно, спроба перезапису гілки шляхом переміщення буде перервана, якщо може призвести до втрати даних. Для примусового переміщення гілки введіть $ git branch -M джерело ціль # замість -m У відмінності від checkout і reset, ці дві команди дають відстрочку у видаленні даних. Зміни залишаються в каталозі.git і можуть бути повернуті відновленням потрібного хешу з `.git/logs` (дивіться вище розділ "Полювання за головами"). За замовчуванням вони будуть зберігатися принаймні два тижні. *Clean*: Деякі команди можуть не спрацювати через побоювання пошкодити невідслідковувані файли. Якщо ви впевнені, що все невідслідковувані файли і каталоги не потрібні, то безжально видаляйте їх командою $ git clean -f -d Наступного разу ця прикра команда спрацює! === Запобігаємо поганим коммітам === Дурні помилки забруднюють мої сховища. Найжахливіше це проблема відсутніх файлів, викликана забутим *git add*. Приклади менш серйозних проступків: завершальні пропуски і невирішені конфлікти злиття. Незважаючи на нешкідливість, я не хотів би, щоб це з'являлося в публічних записах. Якби я тільки поставив захист від дурня, використовуючи _хук_, який би попереджав мене про ці проблеми: $ cd .git/hooks $ cp pre-commit.sample pre-commit # В старих версіях Git: chmod +x pre-commit Тепер Git скасує комміт, якщо виявить зайві пробіли або невирішені конфлікти. Для цього керівництва я в кінці кінців додав наступне в початок хука *pre-commit*, щоб захиститися від своєї неуважності: if git ls-files -o | grep '\.txt$'; then echo ПЕРЕРВАНО! Невідслідковувані .txt файли. exit 1 fi Хукі підтримуються кількома різними операціями Git, дивіться *git help hooks*. Ми використовували приклад хука *post-update* раніше, при обговоренні використання Git через http. Він запускався при кожному переміщенні голови. Простий скрипт post-update оновлює файли, які потрібні Git для зв'язку через засоби повідомлення такі як HTTP. gitmagic-20160304/uk/history.txt0000644000175000017500000004661712666307504015677 0ustar sbadiasbadia== Уроки історії == Внаслідок розподіленої природи Git, історію змін можна легко редагувати. Однак, якщо ви втручаєтеся в минуле, будьте обережні: змінюйте тільки ту частину історії, якою володієте ви і тільки ви. Інакше, як народи вічно з'ясовують, хто ж саме зробив і які безчинства, так і у вас будуть проблеми з примиренням при спробі поєднати різні дерева історії. Деякі розробники переконані, що історія повинна бути незмінна з усіма огріхами та іншим. Інші вважають, що дерева потрібно робити презентабельними перед випуском їх у публічний доступ. Git враховує обидві думки. Переписування історії, як і клонування, розгалуження і злиття, — лише ще одна можливість, яку дає вам Git. Розумне її використання залежить тільки від вас. === Залишаючись коректним === Щойно зробили комміт і зрозуміли, що повинні були ввести інший опис? Запустіть $ git commit --amend щоб змінити останній опис. Усвідомили, що забули додати файл? Запустіть *git add*, щоб це зробити, потім виконайте вищевказану команду. Захотілося додати ще трохи змін в останній комміт? Так зробіть їх і запустіть $ git commit --amend -a === …І ще дещо === Давайте уявимо, що попередня проблема насправді в десять разів гірше. Після тривалої роботи ви зробили ряд коммітів, але ви не дуже-то задоволені тим, як вони організовані, і деякі описи коммітів треба б злегка переформулювати. Тоді запустіть $ git rebase -i HEAD~10 і останні десять коммітів з’являться у вашому улюбленому редакторі (задається змінною оточення $EDITOR). Наприклад: pick 5c6eb73 Додав посилання repo.or.cz pick a311a64 Переставив аналогії в „Працюй як хочеш“ pick 100834f Додав ціль для push в Makefile Старі комміти передують новим коммітам у цьому списку, на відміну від команди `log`. Тут, 5c6eb73 є найстарішим коммітом і 100834f є найновішим. Тепер ви можете: - Видаляти комміти шляхом видалення рядків. Подібно команді revert, але видаляє запис: це буде так ніби комміта ніколи не існувало. - Міняти порядок коммітів, переставляючи рядки. - Заміняти `pick` на: * `edit`, щоб позначати комміт для внесення правок; * `reword`, щоб змінити опис у журналі; * `squash`, щоб злити комміт з попереднім; * `fixup`, щоб злити комміт з попереднім і відкинути його опис. Наприклад, ми могли б замінити другий `pick` з `squash`: pick 5c6eb73 Додав посилання repo.or.cz squash a311a64 Переставив аналогії в „Працюй як хочеш“ pick 100834f Додав ціль для push в Makefile Після того, як ми збережемо і вийдемо, Git зіллє a311a64 у 5c6eb73. Так *squash* зливає у наступний комміт вгорі: думайте ``squash up''. Тоді Git об’єднує повідомлення журналу і подає їх для редагування. Команда *fixup* пропускає цей етап; злиті повідомлення журналу просто відкидаються. Якщо ви позначили комміт командою *edit*, Git поверне вас в минуле, до найстарішого такого комміта. Ви можете відкоректувати старий комміт як описано в попередньому параграфі і, навіть, створити нові комміти, які знаходяться тут. Як тільки ви будете задоволені ``retcon'', йдіть вперед у часі, виконавши: $ git rebase --continue Git виводить комміти до наступного *edit* або до поточного, якщо не залишиться нічого. Ви також можете відмовитися від перебазування (rebase) з: $ git rebase --abort Одним словом, робіть комміти раніше і частіше — ви завжди зможете навести порядок за допомогою rebase. === Локальні зміни зберігаються === Припустимо, ви працюєте над активним проектом. За якийсь час ви робите кілька коммітів, потім синхронізуєте з офіційним деревом через злиття. Цикл повторюється кілька разів, поки ви не будете готові влити зміни в центральне дерево. Проте тепер історія змін в локальному клоні Git являє собою кашу з ваших та офіційних змін. Вам би хотілося бачити всі свої зміни неперервною лінією, а потім — всі офіційні зміни. Це робота для команди *git rebase*, як описано вище. Найчастіше, має сенс використовувати опцію *--onto*, щоб прибрати переплетення. Також дивіться *git help rebase* для отримання детальних прикладів використання цієї чудової команди. Ви можете розщеплювати комміти. Ви можете навіть змінювати порядок гілок у дереві. Будьте обережні: rebase — це потужна команда. Для складних rebases, спочатку зробіть резервну копію за допомогою *git clone*. === Переписуючи історію === Іноді вам може знадобитися в системі керування версіями аналог «замазування» людей на офіційних фотографіях, як би стираючого їх з історії в дусі сталінізму. Наприклад, припустимо, що ми вже збираємося випустити реліз проекту, але він містить файл, який не повинен стати надбанням громадськості з якихось причин. Можливо, я зберіг номер своєї кредитки в текстовий файл і випадково додав його в проект. Видалити файл недостатньо: він може бути доступним зі старих коммітів. Нам треба видалити файл з усіх ревізій: $ git filter-branch --tree-filter 'rm цілком/таємний/файл' HEAD Дивіться *git help filter-branch*, де обговорюється цей приклад і пропонується більш швидкий спосіб вирішення. Взагалі, *filter-branch* дозволяє змінювати істотні частини історії за допомогою однієї-єдиної команди. Після цієї команди каталог +.git/refs/original+ буде описувати стан, який був до її виклику. Переконайтеся, що команда filter-branch зробила те, що ви хотіли, і якщо хочете знову використовувати цю команду, видаліть цей каталог. І, нарешті, замініть клони вашого проекту виправленою версією, якщо збираєтеся надалі з ними працювати. === Створюючи історію === [[makinghistory]] Хочете перевести проект під управління Git? Якщо зараз він знаходиться під управлінням якоїсь із добре відомих систем керування версіями, то цілком імовірно, що хтось вже написав необхідні скрипти для експорту всієї історії проекту в Git. Якщо ні, то дивіться в сторону команди *git fast-import*, яка зчитує текст в спеціальному форматі для створення історії Git з нуля. Зазвичай скрипт, який використовує цю команду, буває зліплений похапцем для одиничного запуску, що переносить весь проект за один раз. В якості прикладу вставте такі рядки в тимчасовий файл, на зразок `/tmp/history`: ---------------------------------- commit refs/heads/master committer Alice Thu, 01 Jan 1970 00:00:00 +0000 data < int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob Tue, 14 Mar 2000 01:59:26 -0800 data < int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- Потім створіть сховище Git з цього тимчасового файлу за допомогою команд: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history Ви можете витягти останню версію проекту за допомогою $ git checkout master . Команда *git fast-export* перетворює будь-яке сховище в формат, зрозумілий для команди *git fast-import*. Її результат можна використовувати як зразок для написання скриптів перетворення або для перенесення сховищ в зрозумілому для людини форматі. Звичайно, за допомогою цих команд можна пересилати сховища текстових файлів через канали передачі тексту. === Коли ж все пішло не так? === Ви тільки що виявили, що деякий функціонал вашої програми не працює, але ви досить чітко пам’ятаєте, що він працював лише кілька місяців тому. Ох ... Звідки ж взялася помилка? Ви ж це перевіряли відразу як розробили. У будь-якому випадку, вже надто пізно. Однак, якщо ви фіксували свої зміни досить часто, то Git зможе точно вказати проблему: $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git витягне стан рівно посередині. Перевірте чи працює те, що зламалося, і якщо все ще ні: $ git bisect bad Якщо ж працює, то замініть "bad" на "good". Git знову перемістить вас в стан посередині між хорошою і поганою версіями, звужуючи коло пошуку. Після декількох ітерацій, цей двійковий пошук приведе вас до того комміту, на якому виникла проблема. Після закінчення розслідування, поверніться у початковий стан командою $ git bisect reset Замість ручного тестування кожної зміни автоматизуйте пошук, запустивши $ git bisect run my_script За поверненим значенням заданої команди, зазвичай одноразового скрипта, Git буде відрізняти хороший стан від поганого. Скрипт повинен повернути 0, якщо теперішній комміт хороший; 125, якщо його треба пропустити, і будь-яке інше число від 1 до 127, якщо він поганий. Від'ємне значення перериває команду bisect. Ви можете зробити багато більше: сторінка допомоги пояснює, як візуалізувати bisect, проаналізувати чи відтворити її журнал, або виключити наперед відомі хороші зміни для прискорення пошуку. === Через кого все пішло не так? === Як і в багатьох інших системах керування версіями, в Git є команда blame: $ git blame bug.c Вона забезпечує кожен рядок вибраного файлу примітками, що розкривають, хто і коли останнім його редагував. На відміну ж від багатьох інших систем керування версіями, ця операція відбувається без з’єднання з мережею, вибираючи дані з локального диску. === Особистий досвід === У централізованих системах керування версіями зміни історії — досить складна операція, і доступна вона лише адміністраторам. Клонування, розгалуження і злиття неможливі без взаємодії по мережі. Так само йдуть справи і з базовими операціями, такими як перегляд історії або фіксація змін. У деяких системах мережеве з’єднання потрібне навіть для перегляду власних змін, або відкриття файлу для редагування. Централізовані системи виключають можливість роботи без мережі і вимагають більш дорогої мережевої інфраструктури, особливо із збільшенням кількості розробників. Що важливіше, всі операції відбуваються повільніше, зазвичай до такої міри, що користувачі уникають користування „просунутими“ командами без крайньої необхідності. У радикальних випадках це стосується навіть більшості базових команд. Коли користувачі змушені запускати повільні команди, продуктивність страждає через переривання робочого процесу. Я відчув цей феномен на собі. Git був моєю першою системою керування версіями. Я швидко звик до нього і став відноситься до його можливостей як до належного. Я припускав, що й інші системи схожі на нього: вибір системи керування версіями не повинен відрізнятися від вибору текстового редактора або переглядача. Коли трохи пізніше я був змушений використовувати централізовану систему керування версіями, я був шокований. Ненадійне інтернет-з’єднання не має великого значення при використанні Git, але робить розробку нестерпною, коли від нього вимагають надійності як у жорсткого диска. На додачу я виявив, що став уникати деяких команд через затримку у їх виконанні, що завадило мені дотримуватися кращого робочого процесу. Коли мені було потрібно запустити повільну команду, порушення ходу моїх думок надавало несумірний збиток розробці. Чекаючи закінчення зв’язку з сервером, я змушений був займатися чимось іншим, щоб згаяти час; наприклад, перевіркою пошти або написанням документації. До того часу, як я повертався до початкової задачі, виконання команди було давно закінчено, але мені доводилося витрачати багато часу, щоб згадати, що саме я робив. Люди не дуже пристосовані для перемикання між завданнями. Крім того, є цікавий ефект „трагедії суспільних ресурсів“: передбачаючи майбутню перевантаженість мережі, деякі люди в спробі запобігти майбутнім затримкам починають використовувати більш широкі канали, ніж їм реально потрібні для поточних завдань. Сумарна активність збільшує завантаження мережі, заохочуючи людей задіяти все більш високошвидкісні канали для запобігання ще більшим затримкам. gitmagic-20160304/uk/translate.txt0000644000175000017500000000371012666307504016156 0ustar sbadiasbadia== Додаток B: Переклад цього керівництва == Я раджу наступний спосіб для перекладу цього керівництва, щоб мої скрипти могли швидко створювати HTML і PDF версії, а всі переклади знаходилися в одному сховищі. Клонуйте вихідні тексти, потім створіть каталог, що відповідає тегу IETF цільової мови: дивіться http://www.w3.org/International/articles/language-tags/Overview.en.php[статтю W3C по інтернаціоналізації]. Наприклад, англійська мова це „en“, а японська — „ja“. Скопіюйте в каталог файли +txt+ з каталогу „en“ і перекладіть їх. Наприклад, для перекладу посібника на http://uk.wikipedia.org/wiki/Клінгонська_мова[клінгонську мову], ви можете набрати: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # «tlh» — мовний код IETF клінгонської мови. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Перекладіть файл. і так із кожним файлом. Відредагуйте Makefile і додайте код мови в змінну TRANSLATIONS. Тепер ви зможете переглядати вашу роботу по ходу справи: $ make tlh $ firefox book.html Почастіше робіть комміти, а коли ваш переклад буде готовий, повідомте мені про це. На GitHub є веб-інтерфейс, що полегшує описані дії: зробіть форк проекту „gitmagic“, залийте ваші зміни і попросіть мене зробити злиття. gitmagic-20160304/uk/intro.txt0000644000175000017500000002772212666307504015325 0ustar sbadiasbadia== Вступне слово == Щоб пояснити, що таке керування версіями, я буду використовувати аналогії. Якщо потрібно більш точне пояснення, зверніться до http://uk.wikipedia.org/wiki/Система_керування_версіями[статті вікіпедії]. === Робота – це гра === Я грав в комп’ютерні ігри майже все своє життя. А ось використовувати системи керування версіями почав вже будучи дорослим. Вважаю, я такий не один, і порівняння цих двох занять може допомогти поясненню і розумінню концепції. Уявіть, що редагування коду або документа – гра. Просунувшись далеко, ви захочете зберегтися. Для цього ви натиснете на кнопку „Зберегти“ у вашому улюбленому редакторі. Але це перезапише стару версію. Це як в стародавніх іграх, де був тільки один слот для збереження: звичайно, ви можете зберегтися, але ви більше ніколи не зможете повернутися до попереднього стану. Це прикро, оскільки попереднє збереження могло вказувати на одне з дуже цікавих місць у грі і, можливо, одного разу ви захочете повернутися до нього. Або, що ще гірше, ви зараз перебуваєте у безвиграшному становищі і змушені починати заново. === Керування версіями === Під час редагування ви можете „Зберегти як ...“ в інший файл або скопіювати файл куди-небудь перед збереженням, щоб уберегти більш старі версії. Можливо, заархівувавши їх для економії місця на диску. Це найпримітивніший вид керування версіями, до того ж він вимагає інтенсивної ручної роботи. Комп'ютерні ігри пройшли цей етап давно, у більшості з них є безліч слотів для збереження з автоматичними тимчасовими мітками. Давайте трохи ускладнимо умови. Нехай у вас є кілька файлів, використовуваних разом, наприклад, вихідний код проекту або файли для вебсайту. Тепер, щоб зберегти стару версію, ви повинні скопіювати весь каталог. Підтримка безлічі таких версій вручну незручна і швидко стає дорогим задоволенням. У деяких іграх збереження – це і є каталог з купою файлів всередині. Ігри приховують деталі від гравця і надають зручний інтерфейс для керування різними версіями цього каталогу. У системах керування версіями все точно так само. У всіх них є приємний інтерфейс для керування каталогом з вашим скарбом. Можете зберігати стан каталога так часто, як забажаєте, а потім відновити будь-яку з попередніх збережених версій. Але, на відміну від комп'ютерних ігор, вони істотно економлять дисковий простір. Зазвичай від версії до версії змінюється тільки кілька файлів і то ненабагато. Зберігання лише відмінностей замість повних копій потребує менше місця. === Розподілене керування === А тепер уявіть дуже складну комп’ютерну гру. Її настільки складно пройти, що безліч досвідчених гравців по всьому світу вирішили об'єднатися і використовувати загальні збереження, щоб спробувати виграти. Проходження на швидкість — живий приклад. Гравці, що спеціалізуються на різних рівнях гри, об’єднуються, щоб в результаті отримати приголомшливий результат. Як би ви організували таку систему, щоб гравці змогли легко отримувати збереження інших? А завантажувати свої? У минулі часи кожен проект використовував централізоване керування версіями. Який-небудь сервер зберігав всі збережені ігри. І ніхто більше. Кожен тримав лише кілька збережень на своїй машині. Коли гравець хотів пройти трохи далі, він завантажував останнє збереження з головного сервера, грав небагато, зберігався і вивантажував вже своє збереження назад на сервер, щоб інші могли ним скористатися. А що якщо гравець з якоїсь причини захотів використовувати більш стару збережену гру? Можливо, нинішнє збереження безвиграшне, бо хтось забув взяти якийсь ігровий предмет ще на третьому рівні, і потрібно знайти останнє збереження, де гру все ще можна закінчити. Або, можливо, хочеться порівняти дві більш старі збережені гри, щоб встановити внесок конкретного гравця. Може бути багато причин повернутися до більш старої версії, але вихід один: потрібно запросити ту стару збережену гру у центрального сервера. Чим більше збережених ігор потрібно, тим більше знадобиться зв’язуватися з сервером. Системи керування версіями нового покоління, до яких відноситься Git, відомі як розподілені системи, їх можна розуміти як узагальнення централізованих систем. Коли гравці завантажуються з головного сервера, вони отримують кожну збережену гру, а не тільки останню. Вони як би дзеркалюють центральний сервер. Ці початкові операції клонування можуть бути ресурсоємними, особливо при довгій історії, але сповна окупаються при тривалій роботі. Найбільш очевидна пряма вигода полягає в тому, що якщо вам навіщось потрібна більш стара версія, взаємодія з сервером не знадобиться. === Дурні забобони === Широко поширена помилка полягає в тому, що розподілені системи непридатні для проектів, які потребують офіційного централізованого сховища. Ніщо не може бути більш далеким від істини. Отримання фотознімку не призводить до того, що ми крадемо чиюсь душу. Точно так само клонування головного сховища не зменшує його важливість. У першому наближенні можна сказати, що все, що робить централізована система керування версіями, добре сконструйована розподілена система може зробити краще. Мережеві ресурси просто дорожчі локальних. Хоча далі ми побачимо, що в розподіленому підході є свої недоліки, ви навряд чи помилитеся у виборі, керуючись цим наближеним правилом. Невеликому проектом може знадобитися лише частинка функціоналу, пропонованого такою системою. Але використання погано масштабованої системи для маленьких проектів подібно використанню римських цифр в розрахунках з невеликими числами. Крім того, проект може вирости понад початкові очікування. Використовувати Git з самого початку — це як тримати напоготові швейцарський ніж, навіть якщо ви всього лише відкриваєте ним пляшки. Одного разу вам шалено знадобиться викрутка і ви будете раді, що під рукою є щось більше, ніж проста відкривачка. === Конфлікти при злитті === Для цієї теми аналогія з комп'ютерною грою стає занадто натягнутою. Замість цього, давайте повернемося до редагування документа. Отже, припустимо, що Марічка вставила рядок на початку файлу, а Іван — в кінці. Обоє вони закачують свої зміни. Більшість систем автоматично зробить розумний висновок: прийняти і об'єднати їх зміни так, щоб обидві правки — і Марічки, і Івана — були застосовані. Тепер припустимо, що і Марічка, і Іван внесли різні зміни в один і той же рядок. У цьому випадку неможливо продовжити без втручання людини. Той із них, хто другим закачає на сервер зміни, буде поінформований про _конфлікте злиття_ (_merge conflict_), і повинен або віддати перевагу одній змінні перед іншою, або скорегувати увесь рядок. Можуть траплятися і більш складні ситуації. Системи керування версіями вирішують прості ситуації самі і залишають складні для людини. Зазвичай таку їхню поведінку можна налаштовувати. gitmagic-20160304/uk/clone.txt0000644000175000017500000004411612666307504015266 0ustar sbadiasbadia== Все про клонування == У старих системах керування версіями стандартна операція для отримання файлів — це checkout. Ви отримуєте набір файлів в конкретному збереженому стані. У Git та інших розподілених системах керування версіями стандартний спосіб — клонування. Для отримання файлів ви створюєте „клон“ всього сховища. Іншими словами, ви фактично створюєте дзеркало центрального сервера. При цьому все, що можна робити з основним сховищем, можна робити і з локальним. === Синхронізація комп'ютерів === Я терпимий до створення архівів або використання *rsync* для резервного копіювання і найпростішої синхронізації. Але я працюю то на ноутбуці, то на стаціонарному комп’ютері, які можуть ніяк між собою не взаємодіяти. Створіть сховище Git і закоммітьте файли на одному комп'ютері. А потім виконайте на іншому $ git clone перший.комп’ютер:/шлях/до/файлів для створення другого примірника файлів і сховища Git. З цього моменту команди $ git commit -a $ git pull інший.комп’ютер:/шлях/до/файлів HEAD будуть „втягувати“ („pull“) стан файлів з іншого комп'ютера на той, де ви працюєте. Якщо ви нещодавно внесли конфліктуючі зміни в один і той же файл, Git дасть вам знати, і потрібно буде зробити комміт заново після вирішення ситуації. === Класичне керування вихідним кодом === Створіть сховище Git для ваших файлів: $ git init $ git add . $ git commit -m "Початковий комміт" На центральному сервері створіть так зване „голе сховище“ („bare repository“) Git в деякому каталозі: $ mkdir proj.git $ cd proj.git $ git --bare init $ touch proj.git/git-daemon-export-ok Запустіть Git-демон, якщо необхідно: $ git daemon --detach # можливо вже запущений Для створення нового порожнього сховища Git на публічних серверах виконуйте їх інструкції. Зазвичай, потрібно заповнити форму на веб-сторінці. Відправте ваші зміни в центральне сховище ось так: $ git push git://центральний.сервер/шлях/до/proj.git HEAD Для отримання ваших вихідних кодів розробник вводить $ git clone git://центральний.сервер/шлях/до/proj.git Після внесення змін розробник зберігає зміни локально: $ git commit -a Для оновлення до останньої версії: $ git pull Будь-які конфлікти злиття потрібно дозволити і закоммітити: $ git commit -a Для вивантаження локальних змін в центральне сховище: $ git push Якщо на головному сервері були нові зміни, зроблені іншими розробниками, команда push не спрацює. У цьому випадку розробнику потрібно буде витягнути до себе (pull) останню версію, вирішити можливі конфлікти зливань і спробувати ще раз. Розробники повинні мати SSH доступ для зазначених вище команд вивантаження та витягування (push та pull). Тим не менш, будь-хто може бачити джерело, набравши: $ git clone git://центральний.сервер/шлях/до/proj.git Власний протокол Git подібний до HTTP: немає аутентифікації, так що кожен може отримати проект. Відповідно, за замовчуванням, вивантаження заборонене через протокол Git. === Таємне джерело (Secret Source) === Для проектів із закритим вихідним кодом опустіть команди доступу і переконайтеся, що ви ніколи не створювали файл з ім'ям `git-daemon-export-ok`. Сховище вже не може бути доступним через протокол Git; тільки ті, хто має доступ SSH можуть побачити його. Якщо всі ваші репозиторії закриті, немає необхідності запускати демон Git оскільки всі зв’язки відбувається через SSH. === Голі сховища (Bare repositories) === Голе сховище називається так тому, що у нього немає робочого каталогу. Воно містить лише файли, які зазвичай приховані в підкаталозі `.git`. Іншими словами, голе сховище містить історію змін, але не містить знімка якоїсь певної версії. Голе сховище грає роль, схожу на роль основного сервера в централізованій системі керування версіями: це дім вашого проекту. Розробники клонують з нього проект і закачують в нього свіжі офіційні зміни. Як правило, воно розташовується на сервері, який не робить майже нічого окрім роздачі даних. Розробка йде в клонах, тому домашнє сховище може обійтися і без робочого каталогу. Багато команд Git не працюють в голих сховищах, якщо змінна середовища `GIT_DIR` не містить шлях до сховища та не зазначений параметр `--bare`. === Push чи pull? === Навіщо вводиться команда push, замість використання вже знайомої pull? Перш за все, pull не працює в голих сховищах, замість неї потрібно використовувати команду 'fetch', яка буде розглянута пізніше. Але навіть якщо тримати на центральному сервері нормальне сховище, використання команди pull в ньому буде складним . Потрібно буде спочатку увійти на сервер інтерактивно і повідомити команді pull адресу машини, з якої ми хочемо забрати зміни. Цьому можуть заважати мережеві брандмауери (firewall), але в першу чергу: що якщо в нас немає інтерактивного доступу до сервера? Тим не менш, не рекомендутся push-ити в сховище крім цього випадку — через плутанину, яка може виникнути, якщо у цільового сховища є робочий каталог. Коротше кажучи, поки вивчаєте Git, push-те лише в голі сховища. В інших випадках pull-те. === Розгалуження проекту === Не подобається шлях розвитку проекту? Думаєте, можете зробити краще? Тоді на своєму сервері виконайте $ git clone git://основний.сервер/шлях/до/файлів Тепер розкажіть усім про вітку проекту на вашому сервері. Пізніше ви зможете в будь-який момент втягнути до себе зміни з початкового проекту: $ git pull === Максимальні бекапи === Хочете мати безліч захищених, географічно відокремлених запасних архівів? Якщо у вашому проекті багато розробників, нічого робити не потрібно! Кожен клон — це і є резервна копія, не тільки поточного стану, але і всієї історії змін проекту. Завдяки криптографічному хешування, пошкодження якого-небудь з клонів буде виявлено при першій же спробі взаємодії з іншими клонами. Якщо ваш проект не такий популярний, знайдіть якомога більше серверів для розміщення клонів. Тим, хто особливо турбується, рекомендується завжди записувати останній 20-байтний SHA1 хеш HEAD у якомусь безпечному місці. Воно має бути безпечним, а не таємним. Наприклад, хороший варіант — публікація в газеті, тому що атакуючому складно змінити кожен примірник газети. === Багатозадачність зі швидкістю світла === Скажімо, ви хочете працювати над декількома функціями паралельно. Тоді закоммітьте ваші зміни і запустіть $ git clone . /якийсь/новий/каталог Завдяки http://en.wikipedia.org/wiki/Hard_link[жорстким посиланням], створення локального клону вимагає менше часу і місця, ніж просте копіювання. Тепер ви можете працювати з двома незалежними функціями одночасно. Наприклад, можна редагувати один клон, поки інший компілюється. У будь-який момент можна зробити комміт і витягнути зміни з іншого клону: $ git pull /другий/клон HEAD === Партизанське керування версіями === Ви працюєте над проектом, який використовує іншу систему керування версіями, і вам дуже не вистачає Git? Тоді створіть сховище Git у своєму робочому каталозі: $ git init $ git add . $ git commit -m "Початковий комміт" потім склонуйте його: $ git clone . /якийсь/новий/каталог Тепер перейдіть в цей новий каталог і працюйте в ньому замість основного, використовуючи Git в своє задоволення. У якийсь момент вам знадобитися синхронізувати зміни з усіма іншими — тоді перейдіть в початковий каталог, синхронізуйте його за допомогою іншої системи керування версіями і наберіть $ git add . $ git commit -m "Синхронізація з рештою" Тепер перейдіть в новий каталог і запустіть $ git commit -a -m "Опис моїх змін" $ git pull Процедура передачі змін іншим залежить від іншої системи керування версіями. Новий каталог містить файли з вашими змінами. Запустіть команди іншої системи керування версіями, необхідні для завантаження файлів в центральне сховище. Subversion (імовірно, найкраща централізована система керування версіями) використовується незліченною кількістю проектів. Команда *git svn* автоматизує описаний процес для сховищ Subversion, а також може бути використана для http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[експорту проекту Git в сховище Subversion]. === Mercurial === Mercurial — схожа система керування версіями, яка може працювати в парі з Git практично без накладок. З розширенням `hg-git` користувач Mercurial може без будь-яких втрат push-ити і pull-ити зі сховища Git. Отримати `hg-git` можна за допомогою Git: $ git clone git://github.com/schacon/hg-git.git або Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ На жаль, мені невідоме аналогічне розширення для Git. Тому я рекомендую використовувати Git, а не Mercurial, для центрального сховища, навіть якщо ви віддаєте перевагу Mercurial. Для проектів, що використовують Mercurial, зазвичай який-небудь доброволець підтримує паралельне сховище Git для залучення користувачів останнього, тоді як проекти, що використовують Git, завдяки `hg-git` автоматично доступні користувачам Mercurial. Хоча розширення може конвертувати сховище Mercurial в Git шляхом push'а в порожнє сховище, цю задачу легше вирішити, використовуючи сценарій `hg-fast-export.sh`, доступний з $ git clone git://repo.or.cz/fast-export.git Для перетворення виконайте в порожньому каталозі $ git init $ hg-fast-export.sh -r /hg/repo після додавання сценарію в ваш `$PATH`. === Bazaar === Згадаємо коротко Bazaar, оскільки це найпопулярніша вільна розподілена система керування версіями після Git і Mercurial. Bazaar відносно молодий, тому у нього є перевага ідущого слідом. Його проектувальники можуть вчитися на помилках попередників і позбутися від історично сформованих недоліків. Крім того, його розробники піклуються про переносимість і взаємодії з іншими системами керування версіями. Розширення `bzr-git` дозволяє (в якійсь мірі) користувачам Bazaar працювати зі сховищами Git. Програма `tailor` конвертує сховища Bazaar в Git і може робити це з накопиченням, тоді як `bzr-fast-export` добре пристосована для разових перетворень. === Чому я використовую Git === Спочатку я вибрав Git тому, що чув, що він в змозі впоратися з абсолютно некерованими вихідними текстами ядра Linux. Я ніколи не відчував потреби змінити його на щось інше. Git працює чудово і мені ще тільки належить напоротися на його недоліки. Так як я в основному використовую Linux, проблеми на інших системах мене не стосуються. Я також віддаю перевагу програмам на C і сценаріям на bash у порівнянні з виконуваними файлами на зразок сценаріїв на Python-і: у них менше залежностей і я звик до швидкого виконання. Я думав про те, як можна поліпшити Git, аж до того, щоб написати власний інструмент, схожий на Git; але лише як академічну вправу. Завершивши проект, я б всеодно продовжив користуватися Git, тому що виграш занадто малий, щоб виправдати використання саморобної системи. Природньо, що ваші потреби та побажання імовірно відрізняються від моїх і ви, можливо, краще уживетеся з іншою системою. І все ж ви не занадто помилитеся, використовуючи Git. gitmagic-20160304/find_selflink.js0000644000175000017500000000155312666307504016151 0ustar sbadiasbadia// From my own website(!) //TODO: only do this for links in the table of contents menu function find_selflink() { var a = document.links; var i = 0; while (i < a.length) { if (a[i].href == document.URL) { var c; var j; var s_new = document.createElement("span"); s_new.className = "currentlink"; c = a[i].childNodes; for (j=0; j Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . gitmagic-20160304/po4gitmagic/0000755000175000017500000000000012666307504015207 5ustar sbadiasbadiagitmagic-20160304/po4gitmagic/README0000644000175000017500000000206212666307504016067 0ustar sbadiasbadiaThis Makefile should make it easy to translate gitmagic with gettext tools on Linux systems. To use it, you have to install the po4a package. To start, clone the gitmagic project. Make the directory corresponding to your language tag and change into it. mkdir xx chdir xx Create a symlink to this Makefile: ln -s ../po4gitmagic/Makefile Makefile Now run make to create the pot folder and all the .po files in there. make gettext Start translating with your prefered .po file editor (e.g. poedit, gtranslator). You need to have 80% of a .po file translated, then you can run: make translate This will create a translated .txt file in your language folder for each .po file that is more than 80% translated. To proof your work change in the gitmagic root folder, run: make xx where xx is the subdirectory name. If something in the original .txt files changed, goto your language folder and run: make update This will update your .po files and mark the changed items as 'fuzzy'. Check the .po files for changes, translate them and run again: make translate gitmagic-20160304/po4gitmagic/Makefile0000644000175000017500000000412212666307504016646 0ustar sbadiasbadia# Makefile to genereate everything needed for a translation of git-magic using po4a SHELL := /bin/bash # the possible targets # # clean - remove the folder $(POTDIR) and all the .txt files # gettext - create the folder $(POTDIR) and generate the .po files in there # translate - create the .txt files from the translated .po files # for success you must have translated already 80% of a .po file # update - update the .po files in case the originals has changed # changed items are marked 'fuzzy' in the .po file to fin them easy .PHONY: clean gettext translate update # the path to the english original .txt files ORGDIR := ../en # the folder where the .po files will be created POTDIR := pot # the filenames for the .txt and .po files # must be identical to the english original .txt files FILES := preface intro basic clone branch history multiplayer grandmaster secrets drawbacks translate # add the .txt suffix to the filenames for a list of .txt files TXTFILES := $(addsuffix .txt, $(FILES)) # add the .po suffix to the filenames for a list of .po files POTFILES := $(addsuffix .po, $(FILES)) # prerequisites for gettext are the .po files gettext: $(addprefix $(POTDIR)/,$(POTFILES)) # prerequisites for translate are the .txt files translate: $(TXTFILES) # no prerequisites to update the translated .po files when the english original .txt has changed update: ( for FILE in $(FILES) ; \ do if [ -f $(ORGDIR)/$$FILE.txt ]; \ then po4a-updatepo -f text -m $(ORGDIR)/$$FILE.txt -M UTF-8 -p $(POTDIR)/$$FILE.po; echo $$FILE; \ fi; \ done ) # remove all .po and .txt files clean: -rm -rf $(POTDIR) -rm -rf *.txt # prerequisites for the .po files is the existance of the pot folder $(POTFILES) : | $(POTDIR) # create the folder for the .po files $(POTDIR) : -mkdir $(POTDIR) # rule how to make the .po files from the english original .txt file $(POTDIR)/%.po : $(ORGDIR)/%.txt po4a-gettextize -f text -m $< -p $@ -M UTF-8 # rule how to make the translatets .txt files from the translated .po files %.txt : $(POTDIR)/%.po po4a-translate -f text -m ../en/$@ -p $< -M UTF-8 -l $@ gitmagic-20160304/de/0000755000175000017500000000000012666307504013370 5ustar sbadiasbadiagitmagic-20160304/de/.gitignore0000644000175000017500000000000412666307504015352 0ustar sbadiasbadia *~ gitmagic-20160304/de/drawbacks.txt0000644000175000017500000002022012666307504016066 0ustar sbadiasbadia== Anhang A: Git's Mängel == Ein paar Git-Probleme habe ich bisher unter den Teppich gekehrt. Einige lassen sich einfach mit Skripten und 'Hooks' lösen, andere erfordern eine Reorganisation oder Neudefinition des gesamten Projekt und für die wenigen verbleibenden Beeinträchtigungen kannst Du nur auf eine Lösung warten. Oder noch besser, anpacken und mithelfen. === SHA1 Schwäche === Mit der Zeit entdecken Kryptographen immer mehr Schwächen an SHA1. Schon heute wäre für finanzkräftige Unternehmen es technisch machbar, Hash-Kollisionen zu finden. In ein paar Jahren hat vielleicht schon ein ganz normaler Heim-PC ausreichend Rechenleistung, um ein Git 'Reopsitory' unbemerkt zu korrumpieren. Hoffentlich stellt Git auf eine bessere Hash Funktion um, bevor die Forschung SHA1 komplett unnütz macht. === Microsoft Windows === Git unter Microsoft Windows kann frustrierend sein: - http://cygwin.com/[Cygwin], eine Linux ähnliche Umgebung für Windows, enthält http://cygwin.com/packages/git/[eine Windows Portierung von Git]. - http://code.google.com/p/msysgit/[Git unter MSys] ist eine Alternative, die sehr wenig Laufzeitunterstützung erfordert, jedoch bedürfen einige Kommandos noch einer Überarbeitung. === Dateien ohne Bezug === Wenn Dein Projekt sehr groß ist und viele Dateien enthält, die in keinem direkten Bezug stehen, trotzdem aber häufig geändert werden, kann Git nachteiliger sein als andere Systeme, weil es keine einzelnen Dateien überwacht. Git überwacht immer das ganze Projekt, was normalerweise schon von Vorteil ist. Eine Lösung ist es, Dein Projekt in kleinere Stücke aufzuteilen, von denen jedes nur die in Beziehung stehenden Dateien enthält. Benutze *git submodule* wenn Du trotzdem alles in einem einzigen 'Repository' halten willst. === Wer macht was? === Einige Versionsverwaltungssysteme zwingen Dich explizit, eine Datei auf irgendeine Weise für die Bearbeitung zu kennzeichnen. Obwohl es extrem lästig ist, wenn es die Kommunikation mit einem zentralen Server erfordert, so hat es doch zwei Vorteile: 1. Unterschiede sind schnell gefunden, weil nur die markierten Dateien untersucht werden müssen. 2. Jeder kann herausfinden wer sonst gerade an einer Datei arbeitet, indem er beim zentralen Server anfragt, wer die Datei zum Bearbeiten markiert hat. Mit geeigneten Skripten kannst Du das auch mit Git hinkriegen. Das erfordert aber die Mitarbeit der Programmierer, denn sie müssen die Skripte auch aufrufen, wenn sie eine Datei bearbeiten. === Dateihistorie === Da Git die Änderungen über das gesamte Projekt aufzeichnet, erfordert die Rekonstruktion des Verlaufs einer einzelnen Datei mehr Aufwand als in Versionsverwaltungssystemen, die einzelne Dateien überwachen. Die Nachteile sind üblicherweise gering und werden gern in Kauf genommen, da andere Operationen dafür unglaublich effizient sind. Zum Beispiel ist `git checkout` schneller als `cp -a`, und projektweite Unterschiede sind besser zu komprimieren als eine Sammlung von Änderungen auf Dateibasis. === Der erster Klon === Einen Klon zu erstellen ist aufwendiger als in anderen Versionsverwaltungssystemen, wenn ein längerer Verlauf existiert. Der initiale Aufwand lohnt sich aber auf längere Sicht, da die meisten zukünftigen Operationen dann schnell und offline erfolgen. Trotzdem gibt es Situationen, in denen es besser ist, einen oberflächlichen Klon mit der `--depth` Option zu erstellen. Das geht wesentlich schneller, aber der resultierende Klon hat nur eingeschränkte Funktionalität. === Unbeständige Projekte === Git wurde geschrieben um schnell zu sein, im Hinblick auf die Größe der Änderungen. Leute machen kleine Änderungen von Version zu Version. Ein einzeiliger Bugfix hier, eine neue Funktion da, verbesserte Kommentare und so weiter. Aber wenn sich Deine Dateien zwischen aufeinanderfolgenden Versionen gravierend ändern, dann wird zwangsläufig mit jedem 'Commit' Dein Verlauf um die Größe des gesamten Projekts wachsen. Es gibt nichts, was irgendein Versionsverwaltungssystem dagegen machen kann, aber der Standard Git Anwender leidet mehr darunter, weil normalerweise der ganze Verlauf geklont wird. Die Ursachen für die großen Unterschiede sollten ermittelt werden. Vielleicht können Dateiformate geändert werden. Kleinere Bearbeitungen sollten auch nur minimale Änderungen an so wenig Dateien wie möglich bewirken. Vielleicht ist eher eine Datenbank oder Sicherungs-/Archivierungslösung gesucht, nicht ein Versionsverwaltungssystem. Ein Versionsverwaltungssystem zum Beispiel ist eine ungeeignete Lösung um Fotos zu verwalten, die periodisch von einer Webcam gemacht werden. Wenn die Dateien sich tatsächlich konstant verändern, und sie wirklich versioniert werden müssen, ist es eine Möglichkeit, Git in zentralisierter Form zu verwenden. Jeder kann oberflächliche Klone erstellen, die nur wenig oder gar nichts vom Verlauf des Projekts enthalten. Natürlich sind dann viele Git Funktionen nicht verfügbar, und Änderungen müssen als 'Patches' übermittelt werden. Das funktioniert wahrscheinlich ganz gut, wenn auch unklar ist, warum jemand die Versionsgeschichte von wahnsinnig instabilen Dateien braucht. Ein anderes Beispiel ist ein Projekt, das von Firmware abhängig ist, welche die Form einer großen Binärdatei annimmt. Der Verlauf der Firmware interessiert den Anwender nicht und Änderungen lassen sich schlecht komprimieren, so blähen Firmwarerevisionen die Größe des 'Repository' unnötig auf. In diesem Fall sollte der Quellcode der Firmware in einem Git 'Repository' gehalten werden und die Binärdatei außerhalb des Projekts. Um das Leben zu vereinfachen, könnte jemand ein Skript erstellen, das Git benutzt, um den Quellcode zu klonen und 'rsync' oder einen oberflächlichen Klon für die Firmware. === Globaler Zähler === Verschiedene Versionsverwaltungssysteme unterhalten einen Zähler, der mit jedem 'Commit' erhöht wird. Git referenziert Änderungen anhand ihres SHA1-Hash, was in vielen Fällen besser ist. Aber einige Leute sind diesen Zähler gewöhnt. Zum Glück ist es einfach, Skripte zu schreiben, sodass mit jedem Update das zentrale Git 'Repository' einen Zähler erhöht. Vielleicht in Form eines 'Tags', der mit dem SHA1-Hash des letzten 'Commit' verknüpft ist. Jeder Klon könnte einen solchen Zähler bereitstellen, aber der wäre vermutlich nutzlos, denn nur der Zähler des zentralen 'Repository' ist für alle relevant. === Leere Unterverzeichnisse === Leere Unterverzeichnisse können nicht überwacht werden. Erstelle eine Dummy-Datei, um dieses Problem zu umgehen. Die aktuelle Implementierung von Git, weniger sein Design, ist verantwortlich für diesen Pferdefuß. Mit etwas Glück, wenn Git's Verbreitung zunimmt und mehr Anwender nach dieser Funktion verlangen, wird sie vielleicht implementiert. === Initialer 'Commit' === Ein klischeehafter Computerwissenschaftler zählt von 0 statt von 1. Leider, bezogen auf 'Commits', hält sich Git nicht an diese Konvention. Viele Kommandos sind mürrisch vor dem intialen 'Commit'. Zusätzlich müssen verschiedene Grenzfälle speziell behandelt werden, wie der 'Rebase' eines 'Branch' mit einem abweichenden initialen 'Commit'. Git würde davon provitieren, einen Null-'Commit' zu definieren: sofort nach dem Erstellen eines 'Repository' wird der 'HEAD' auf eine Zeichenfolge von 20 Null-Bytes gesetzt. Dieser spezielle 'Commit' repräsentiert einen leeren 'Tree', ohne Eltern, irgendwann vielleicht der Vorfahr aller Git 'Repositories'. Würde dann zum Beispiel *git log* ausgeführt, würde der Anwender darüber informiert, dass noch keine 'Commits' gemacht wurden, anstelle mit einem fatalen Fehler zu beenden. Das gilt stellvertretend für andere Anweisungen. Jeder initiale 'Commit' ist dann stillschweigend ein Abkömmling dieses Null-'Commits'. Leider gibt es noch ein paar Problemfälle. Wenn mehrere 'Branches' mit unterschiedlichen initialen 'Commits' zusammengeführt und dann ein 'Rebase' gemacht wird, ist ein manuelles Eingreifen erforderlich. === Eigenarten der Anwendung === Für die 'Commits' A und B, hängt die Bedeutung der Ausdrücke "A..B" und "A...B" davon ab, ob eine Anweisung zwei Endpunkte erwartet oder einen Bereich. Siehe *git help diff* und *git help rev-parse*. gitmagic-20160304/de/branch.txt0000644000175000017500000003205012666307504015366 0ustar sbadiasbadia== 'Branch'-Magie == Unverzügliches 'Branchen' und 'Mergen' sind die hervorstechenden Eigenschaften von Git. *Problem*: Externe Faktoren zwingen zum Wechsel des Kontext. Ein schwerwiegender Fehler in der veröffentlichten Version tritt ohne Vorwarnung auf. Die Frist für ein bestimmtes Leistungsmerkmal rückt näher. Ein Entwickler, dessen Unterstützung für eine Schlüsselstelle im Projekt wichtig ist, verlässt das Team. In allen Fällen musst Du alles stehen und liegen lassen und Dich auf eine komplett andere Aufgabe konzentrieren. Den Gedankengang zu unterbrechen ist schlecht für die Produktivität und je komplizierter der Kontextwechsel ist, desto größer ist der Verlust. Mit zentraler Versionsverwaltung müssen wir eine neue Arbeitskopie vom Server herunterladen. Bei verteilen Systemen ist das viel besser, da wir die benötigt Version lokal 'clonen' können. Doch das 'Clonen' bringt das Kopieren des gesamten Arbeitsverzeichnis wie auch die ganze Geschichte bis zum angegebenen Punkt mit sich. Auch wenn Git die Kosten durch Dateifreigaben und Verknüpfungen reduziert, müssen doch die gesamten Projektdateien im neuen Arbeitsverzeichnis erstellt werden. *Lösung*: Git hat ein besseres Werkzeug für diese Situationen, die wesentlich schneller und platzsparender als 'clonen' ist: *git branch*. Mit diesem Zauberwort verwandeln sich die Dateien in Deinem Arbeitsverzeichnis plötzlich von einer Version in eine andere. Diese Verwandlung kann mehr als nur in der Geschichte vor und zurück gehen. Deine Dateien können sich verwandeln, vom aktuellsten Stand, zur experimentellen Version, zum neusten Entwicklungsstand, zur Version Deines Freundes und so weiter. === Die Chef-Taste === Hast Du schon einmal ein Spiel gespielt, wo beim Drücken einer Taste (``der Chef-Taste''), der Monitor sofort ein Tabellenblatt oder etwas anderes angezeigt hat? Dass, wenn der Chef ins Büro spaziert, während Du das Spiel spielst, Du es schnell verstecken kannst? In irgendeinem Verzeichnis: $ echo "Ich bin klüger als mein Chef" > meinedatei.txt $ git init $ git add . $ git commit -m "Erster Stand" Wir haben ein Git 'Repository' erstellt, das eine Textdatei mit einer bestimmten Nachricht enthält. Nun gib ein: $ git checkout -b chef # scheinbar hat sich danach nichts geändert $ echo "Mein Chef ist klüger als ich" > meinedatei.txt $ git commit -a -m "Ein anderer Stand" Es sieht aus, als hätten wir unsere Datei überschrieben und 'commitet'. Aber es ist eine Illusion. Tippe: $ git checkout master # wechsle zur Originalversion der Datei und Simsalabim! Die Textdatei ist wiederhergestellt. Und wenn der Chef in diesem Verzeichnis herumschnüffelt, tippe: $ git checkout chef # wechsle zur Version die der Chef ruhig sehen kann Du kannst zwischen den beiden Versionen wechseln, so oft du willst und du kannst unabhängig voneinander in jeder Version Änderungen 'commiten' === Schmutzarbeit === [[branch]] Sagen wir, Du arbeitest an einer Funktion und Du musst, warum auch immer, drei Versionen zurückgehen, um ein paar print Anweisungen einzufügen, damit Du siehst, wie etwas funktioniert. Dann: $ git commit -a $ git checkout HEAD~3 Nun kannst Du überall wild temporären Code hinzufügen. Du kannst diese Änderungen sogar 'commiten'. Wenn Du fertig bist, $ git checkout master um zur ursprünglichen Arbeit zurückzukehren. Beachte, dass alle Änderungen, die nicht 'commitet' sind, übernommen werden. Was, wenn Du am Ende die temporären Änderungen sichern willst? Einfach: $ git checkout -b schmutzig und 'commite', bevor Du auf den 'Master Branch' zurückschaltest. Wann immer Du zu Deiner Schmutzarbeit zurückkehren willst, tippe einfach: $ git checkout schnmutzig Wir sind mit dieser Anweisung schon in einem früheren Kapitel in Berührung gekommen, als wir das Laden alter Stände besprochen haben. Nun können wir die ganze Geschichte erzählen: Die Dateien ändern sich zu dem angeforderten Stand, aber wir müssen den 'Master Branch' verlassen. Jeder 'Commit' ab jetzt führt Deine Dateien auf einen anderen Weg, dem wir später noch einen Namen geben können. Mit anderen Worten, nach dem Abrufen eines alten Stands versetzt Dich Git automatisch in einen neuen, unbenannten 'Branch', der mit *git checkout -b* benannt und gesichert werden kann. === Schnelle Fehlerbehebung === Du steckst mitten in der Arbeit, als es heißt, alles fallen zu lassen, um einen neu entdeckten Fehler in 'Commit' `1b6d...` zu beheben: $ git commit -a $ git checkout -b fixes 1b6d Dann, wenn Du den Fehler behoben hast: $ git commit -a -m "Fehler behoben" $ git checkout master und fahre mit Deiner ursprünglichen Arbeit fort. Du kannst sogar die frisch gebackene Fehlerkorrektur auf Deinen aktuellen Stand übernehmen: $ git merge fixes === 'Mergen' === Mit einigen Versionsverwaltungssystemen ist das Erstellen eines 'Branch' einfach, aber das Zusammenfügen ('Mergen') ist schwierig. Mit Git ist 'Mergen' so einfach, dass Du gar nicht merkst, wenn es passiert. Tatsächlich sind wir dem 'Mergen' schon lange begegnet. Die *pull* Anweisung holt ('fetch') eigentlich die 'Commits' und verschmilzt ('merged') diese dann mit dem aktuellen 'Branch'. Wenn Du keine lokalen Änderungen hast, dann ist 'merge' eine 'schnelle Weiterleitung', ein Ausnahmefall, ähnlich dem Abrufen der letzten Version eines zentralen Versionsverwaltungssystems. Wenn Du aber Änderungen hast, wird Git diese automatisch 'mergen' und Dir Konflikte melden. Normalerweise hat ein 'Commit' genau einen Eltern-'Commit', nämlich den vorhergehenden 'Commit'. Das 'Mergen' mehrerer 'Branches' erzeugt einen 'Commit' mit mindestens zwei Eltern. Das wirft die Frage auf: Welchen 'Commit' referenziert `HEAD~10` tatsächlich? Ein 'Commit' kann mehrere Eltern haben, welchem folgen wir also? Es stellt sich heraus, dass diese Notation immer den ersten Elternteil wählt. Dies ist erstrebenswert, denn der aktuelle 'Branch' wird zum ersten Elternteil während eines 'Merge'; häufig bist Du nur von Änderungen betroffen, die Du im aktuellen 'Branch' gemacht hast, als von den Änderungen, die von anderen 'Branches' eingebracht wurden. Du kannst einen bestimmten Elternteil mit einem Caret-Zeichen referenzieren. Um zum Beispiel die Logs vom zweiten Elternteil anzuzeigen: $ git log HEAD^2 Du kannst die Nummer für den ersten Elternteil weglassen. Um zum Beispiel die Unterschiede zum ersten Elternteil anzuzeigen: $ git diff HEAD^ Du kannst diese Notation mit anderen Typen kombinieren. Zum Beispiel: $ git checkout 1b6d^^2~10 -b uralt beginnt einen neuen 'Branch' ``uralt'', welcher den Stand 10 'Commits' zurück vom zweiten Elternteil des ersten Elternteil des 'Commits', dessen Hashwert mit 1b6d beginnt. === Kontinuierlicher Arbeitsfluss === In Herstellungsprozessen muss der zweite Schritt eines Plans oft auf die Fertigstellung des ersten Schritt warten. Ein Auto, das repariert werden soll, steht unbenutzt in der Garage bis ein Ersatzteil geliefert wird. Ein Prototyp muss warten, bis ein Baustein fabriziert wurde, bevor die Konstruktion fortgesetzt werden kann. Bei Softwareprojekten kann das ähnlich sein. Der zweite Teil eines Leistungsmerkmals muss warten, bis der erste Teil veröffentlicht und getestet wurde. Einige Projekte erfordern, dass Dein Code überprüft werden muss, bevor er akzeptiert wird; Du musst also warten, bis der erste Teil geprüft wurde, bevor Du mit dem zweiten Teil anfangen kannst. Dank des schmerzlosen 'Branchen' und 'Mergen' können wir die Regeln beugen und am Teil II arbeiten, bevor Teil I offiziell freigegeben wurde. Angenommen Du hast Teil I 'commitet' und zur Prüfung eingereicht. Sagen wir, Du bist im `master` 'Branch'. Dann 'branche' zu Teil II: $ git checkout -b teil2 Du arbeitest also an Teil II und 'commitest' Deine Änderungen regelmäßig. Irren ist menschlich und so kann es vorkommen, dass Du zurück zu Teil I willst, um einen Fehler zu beheben. Wenn Du Glück hast oder sehr gut bist, kannst Du die nächsten Zeilen überspringen. $ git checkout master # Gehe zurück zu Teil I. $ fix_problem $ git commit -a # 'Commite' die Lösung. $ git checkout teil2 # Gehe zurück zu Teil II. $ git merge master # 'Merge' die Lösung. Schließlich, Teil I ist zugelassen: $ git checkout master # Gehe zurück zu Teil I. $ submit files # Veröffentliche deine Dateien! $ git merge teil2 # 'Merge' in Teil II. $ git branch -d teil2 # Lösche den Branch "teil2" Nun bist Du wieder im `master` 'Branch', mit Teil II im Arbeitsverzeichnis. Es ist einfach, diesen Trick auf eine beliebige Anzahl von Teilen zu erweitern. Es ist genauso einfach, rückwirkend zu 'branchen': angenommen, Du merkst zu spät, dass vor sieben 'Commits' ein 'Branch' erforderlich gewesen wäre. Dann tippe: $ git branch -m master teil2 # Umbenennen des 'Branch' "master" zu "teil2". $ git branch master HEAD~7 # Erstelle neuen "master", 7 Commits voraus Der `master` Branch enthält nun Teil I, und der `teil2` Branch enthält den Rest. Wir befinden uns in letzterem Branch; wir haben `master` erzeugt, ohne dorthin zu wechseln, denn wir wollen im `teil2` weiterarbeiten. Das ist unüblich. Bisher haben wir unmittelbar nach dem Erstellen in einen 'Branch' gewechselt, wie in: $ git checkout HEAD~7 -b master # erzeuge einen Branch, und wechsle zu ihm. === Mischmasch Reorganisieren === Vielleicht magst Du es, alle Aspekte eines Projekts im selben 'Branch' abzuarbeiten. Du willst deine laufenden Arbeiten für Dich behalten und andere sollen Deine 'Commits' nur sehen, wenn Du sie hübsch organisiert hast. Beginne ein paar 'Branches': $ git branch sauber # Erzeuge einen Branch für gesäuberte Commits. $ git checkout -b mischmasch # Erzeuge und wechsle in den Branch zum Arbeiten. Fahre fort, alles zu bearbeiten: Behebe Fehler, füge Funktionen hinzu, erstelle temporären Code und so weiter und 'commite' Deine Änderungen oft. Dann: $ git checkout bereinigt $ git cherry-pick mischmasch^^ wendet den Urahn des obersten 'Commit' des ``mischmasch'' 'Branch' auf den ``bereinigt'' 'Branch' an. Durch das Herauspicken der Rosinen kannst Du einen 'Branch' konstruieren, der nur endgültigen Code enthält und zusammengehörige 'Commits' gruppiert hat. === 'Branches' verwalten === Ein Liste aller 'Branches' bekommst Du mit: $ git branch Standardmäßig beginnst Du in einem 'Branch' namens ``master''. Einige plädieren dafür, den ``master'' 'Branch' unangetastet zu lassen und für seine Arbeit einen neuen 'Branch' anzulegen. Die *-d* und *-m* Optionen erlauben dir 'Branches' zu löschen und zu verschieben (umzubenennen). Siehe *git help branch*. Der ``master'' 'Branch' ist ein nützlicher Brauch. Andere können davon ausgehen, dass Dein 'Repository' einen 'Branch' mit diesem Namen hat und dass er die offizielle Version enthält. Auch wenn Du den ``master'' 'Branch' umbenennen oder auslöschen könntest, kannst Du diese Konvention aber auch respektieren. === Temporäre 'Branches' === Nach einer Weile wirst Du feststellen, dass Du regelmäßig kurzlebige 'Branches' erzeugst, meist aus dem gleichen Grund: jeder neue 'Branch' dient lediglich dazu, den aktuellen Stand zu sichern, damit Du kurz zu einem alten Stand zurück kannst, um eine vorrangige Fehlerbehebung zu machen oder irgendetwas anderes. Es ist vergleichbar mit dem kurzzeitigen Umschalten des Fernsehkanals, um zu sehen, was auf dem anderen Kanal los ist. Doch anstelle ein paar Knöpfe zu drücken, machst du 'create', 'checkout', 'merge' und 'delete' von temporären 'Branches'. Glücklicherweise hat Git eine Abkürzung dafür, die genauso komfortabel ist wie eine Fernbedienung: $ git stash Das sichert den aktuellen Stand an einem temporären Ort ('stash'=Versteck) und stellt den vorherigen Stand wieder her. Dein Arbeitsverzeichnis erscheint wieder exakt in dem Zustand, wie es war, bevor Du anfingst zu editieren. Nun kannst Du Fehler beheben, Änderungen vom zentralen 'Repository' holen ('pull') und so weiter. Wenn Du wieder zurück zu Deinen Änderungen willst, tippe: $ git stash apply # Es kann sein, dass Du Konflikte auflösen musst. Du kannst mehrere 'stashes' haben und diese unterschiedlich handhaben. Siehe *git help stash*. Wie Du Dir vielleicht schon gedacht hast, verwendet Git 'Branches' im Hintergrund, um diesen Zaubertrick durchzuführen. === Arbeite, wie Du willst === Du magst Dich fragen, ob 'Branches' diesen Aufwand wert sind. Immerhin sind 'Clone' fast genauso schnell, und Du kannst mit *cd* anstelle von esoterischen Git Befehlen zwischen ihnen wechseln. Betrachten wir Webbrowser. Warum mehrere Tabs unterstützen und mehrere Fenster? Weil beides zu erlauben, eine Vielzahl an Stilen unterstützt. Einige Anwender möchten nur ein Browserfenster geöffnet haben und benutzen Tabs für unterschiedliche Webseiten. Andere bestehen auf dem anderen Extrem: mehrere Fenster, ganz ohne Tabs. Wieder andere bevorzugen irgendetwas dazwischen. 'Branchen' ist wie Tabs für dein Arbeitsverzeichnis, und 'Clonen' ist wie das Öffnen eines neuen Browserfenster. Diese Operationen sind schnell und lokal, also warum nicht damit experimentieren, um die beste Kombination für sich selbst zu finden? Git lässt Dich genauso arbeiten, wie Du es willst. gitmagic-20160304/de/secrets.txt0000644000175000017500000003334412666307504015610 0ustar sbadiasbadia== Aufgedeckte Geheimnisse == Wir werfen einen Blick unter die Motorhaube und erklären, wie Git seine Wunder vollbringt. Ich werde nicht ins Detail gehen. Für tiefer gehende Erklärungen verweise ich auf das http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[englischsprachige Benutzerhandbuch]. === Unsichtbarkeit === Wie kann Git so unauffällig sein? Abgesehen von gelegentlichen 'Commits' und 'Merges' kannst Du arbeiten, als würde die Versionsverwaltung nicht existieren. Das heißt, bis Du sie brauchst. Und das ist, wenn Du froh bist, dass Git die ganze Zeit über Dich gewacht hat. Andere Versionsverwaltungssysteme zwingen Dich ständig, Dich mit Verwaltungskram und Bürokratie herumzuschlagen. Dateien können schreibgeschützt sein, bis Du einem zentralen Server mitteilst, welche Dateien Du gerne bearbeiten möchtest. Die einfachsten Befehle werden bis zum Schneckentempo verlangsamt, wenn die Anzahl der Anwender steigt. Deine Arbeit kommt zum Stillstand, wenn das Netzwerk oder der zentrale Server weg sind. Im Gegensatz dazu hält Git seinen Verlauf einfach im `.git` Verzeichnis von Deinem Arbeitsverzeichnis. Das ist Deine eigene Kopie der Versionsgeschichte, damit kannst Du so lange offline bleiben, bis Du mit anderen kommunizieren willst. Du hast die absolute Kontrolle über das Schicksal Deiner Dateien, denn Git kann jederzeit einfach einen gesicherten Stand aus `.git` wiederherstellen. === Integrität === Die meisten Leute verbinden mit Kryptographie die Geheimhaltung von Informationen, aber ein genau so wichtiges Ziel ist es, Informationen zu sichern. Die richtige Anwendung von kryptographischen Hash-Funktionen kann einen versehentlichen oder bösartigen Datenverlust verhindern. Einen SHA1-Hash-Wert kann man sich als eindeutige 160-Bit Identitätsnummer für jegliche Zeichenkette vorstellen, welche Dir in Deinem ganzen Leben begegnen wird. Sogar mehr als das: jegliche Zeichenfolge, die alle Menschen über mehrere Generationen verwenden. Ein SHA1-Hash-Wert selbst ist eine Zeichenfolge von Bytes. Wir können SHA1-Hash-Werte aus Zeichenfolgen generieren, die selbst SHA1-Hash-Werte enthalten. Diese einfache Beobachtung ist überraschend nützlich: suche nach 'hash chains'. Wir werden später sehen, wie Git diese nutzt um effizient die Datenintegrität zu garantieren. Kurz gesagt, Git hält Deine Daten in dem `.git/objects` Unterverzeichnis, wo Du anstelle von normalen Dateinamen nur Identitätsnummern findest. Durch die Verwendung von Identitätsnummern als Dateiname, zusammen mit ein paar Sperrdateien und Zeitstempeltricks, macht Git aus einem einfachen Dateisystem eine effiziente und robuste Datenbank. === Intelligenz === Woher weiß Git, dass Du eine Datei umbenannt hast, obwohl Du es ihm niemals explizit mitgeteilt hast? Sicher, Du hast vielleicht *git mv* benutzt, aber das ist exakt das selbe wie *git rm* gefolgt von *git add*. Git stöbert Umbenennungen und Kopien zwischen aufeinander folgenden Versionen heuristisch auf. Vielmehr kann es sogar Codeblöcke erkennen, die zwischen Dateien hin und her kopiert oder verschoben wurden! Jedoch kann es nicht alle Fälle abdecken, aber es leistet ordentliche Arbeit und diese Eigenschaft wird immer besser. Wenn es bei Dir nicht funktioniert, versuche Optionen zur aufwendigeren Erkennung von Kopien oder erwäge einen Upgrade. === Indizierung === Für jede überwachte Datei speichert Git Informationen wie deren Größe, ihren Erstellzeitpunkt und den Zeitpunkt der letzten Bearbeitung in einer Datei die wir als 'Index' kennen. Um zu ermitteln, ob eine Datei verändert wurde, vergleicht Git den aktuellen Status mit dem im Index gespeicherten. Stimmen diese Daten überein, kann Git das Lesen des Dateiinhalts überspringen. Da das Abfragen des Dateistatus erheblich schneller ist als das Lesen der Datei, kann Git, wenn Du nur ein paar Dateien verändert hast, seinen Status im Nu aktualisieren. Wir haben früher festgestellt, dass der Index ein Bereitstellungsraum ist. Warum kann ein Haufen von Dateistatusinformationen ein Bereitstellungsraum sein? Weil die 'add' Anweisung Dateien in die Git Datenbank befördert und die Dateistatusinformationen aktualisiert, während die 'commit' Anweisung, ohne Optionen, einen 'Commit' nur auf Basis der Dateistatusinformationen erzeugt, weil die Dateien ja schon in der Datenbank sind. === Git's Wurzeln === Dieser http://lkml.org/lkml/2005/4/6/121['Linux Kernel Mailing List' Beitrag] beschreibt die Kette von Ereignissen, die zu Git geführt haben. Der ganze Beitrag ist eine faszinierende archäologische Seite für Git Historiker. === Die Objektdatenbank === Jegliche Versionen Deiner Daten wird in der Objektdatenbank gehalten, welche im Unterverzeichnis `.git/objects` liegt; Die anderen Orte in `.git/` enthalten weniger wichtige Daten: den Index, 'Branch' Namen, Bezeichner ('tags'), Konfigurationsoptionen, Logdateien, die Position des aktuellen 'HEAD Commit' und so weiter. Die Objektdatenbank ist einfach aber trotzdem elegant, und sie ist die Quelle von Git's Macht. Jede Datei in `.git/objects` ist ein 'Objekt'. Es gibt drei Arten von Objekten, die uns betreffen: 'Blob'-, 'Tree'- und 'Commit'-Objekte. === Blobs === Zuerst ein Zaubertrick. Suche Dir irgendeinen Dateinamen aus. In einem leeren Verzeichnis: $ echo sweet > DEIN_DATEINAME $ git init $ git add . $ find .git/objects -type f Du wirst folgendes sehen: +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. Wie konnte ich das wissen, ohne den Dateiname zu kennen? Weil der SHA1-Hash-Wert von: "blob" SP "6" NUL "sweet" LF aa823728ea7d592acc69b36875a482cdf3fd5c8d ist. Wobei SP ein Leerzeichen ist, NUL ist ein Nullbyte und LF ist ein Zeilenumbruch. Das kannst Du durch die Eingabe von $ printf "blob 6\000sweet\n" | sha1sum kontrollieren. Git ist 'assoziativ': Dateien werden nicht nach Ihren Namen gespeichert, sondern eher nach dem SHA1-Hash-Wert der Daten, welche sie enthalten, in einer Datei, die wir als 'Blob'-Objekt bezeichnen. Wir können uns den SHA1-Hash-Wert als eindeutige Identnummer des Dateiinhalts vorstellen, was sinngemäß bedeutet, dass die Dateien über ihren Inhalt adressiert werden. Das führende `blob 6` ist lediglich ein Vermerk, der sich aus dem Objekttyp und seiner Länge in Bytes zusammensetzt; er vereinfacht die interne Verwaltung. So konnte ich einfach vorhersagen, was Du sehen wirst. Der Dateiname ist irrelevant: nur der Dateiinhalt wird zum Erstellen des 'Blob'-Objekt verwendet. Du wirst Dich fragen, was mit identischen Dateien ist. Versuche Kopien Deiner Datei hinzuzufügen, mit beliebigen Dateinamen. Der Inhalt von +.git/objects+ bleibt der selbe, ganz egal wieviele Dateien Du hinzufügst. Git speichert den Dateiinhalt nur ein einziges Mal. Übrigens, die Dateien in +.git/objects+ sind mit zlib komprimiert, Du solltest sie also nicht direkt anschauen. Filtere sie durch http://www.zlib.net/zpipe.c[zpipe -d], oder gib ein: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d was Dir das Objekt im Klartext anzeigt. === 'Trees' === Aber wo sind die Dateinamen? Sie müssen irgendwo gespeichert sein. Git kommt beim 'Commit' dazu, sich um die Dateinamen zu kümmern: $ git commit # Schreibe eine Bemerkung. $ find .git/objects -type f Du solltest nun drei Objekte sehen. Dieses mal kann ich Dir nicht sagen, wie die zwei neuen Dateien heißen, weil es zum Teil vom gewählten Dateiname abhängt, den Du ausgesucht hast. Fahren wir fort mit der Annahme, Du hast eine Datei ``rose'' genannt. Wenn nicht, kannst Du den Verlauf so umschreiben, dass es so aussieht, als hättest Du es: $ git filter-branch --tree-filter 'mv DEIN_DATEINAME rose' $ find .git/objects -type f Nun müsstest Du die Datei +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+ sehen, denn das ist der SHA1-Hash-Wert ihres Inhalts: "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Prüfe, ob diese Datei tatsächlich dem obigen Inhalt entspricht, durch Eingabe von: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch Mit zpipe ist es einfach, den SHA1-Hash-Wert zu prüfen: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum Die SHA1-Hash-Wert Prüfung mit 'cat-file' ist etwas kniffliger, da dessen Ausgabe mehr als die rohe unkomprimierte Objektdatei enthält. Diese Datei ist ein 'Tree'-Objekt: eine Liste von Datensätzen, bestehend aus dem Dateityp, dem Dateinamen und einem SHA1-Hash-Wert. In unserem Beispiel ist der Dateityp 100644, was bedeutet, dass `rose` eine normale Datei ist, und der SHA1-Hash-Wert entspricht dem 'Blob'-Objekt, welches den Inhalt von `rose` enthält. Andere mögliche Dateitypen sind ausführbare Programmdateien, symbolische Links oder Verzeichnisse. Im letzten Fall zeigt der SHA1-Hash-Wert auf ein 'Tree'-Objekt. Wenn Du 'filter-branch' aufrufst, bekommst Du alte Objekte, welche nicht länger benötigt werden. Obwohl sie automatisch über Bord geworfen werden, wenn ihre Gnadenfrist abgelaufen ist, wollen wir sie nun löschen, damit wir unserem Beispiel besser folgen können. $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune Für reale Projekte solltest Du solche Anweisungen üblicherweise vermeiden, da Du dadurch Datensicherungen zerstörst. Wenn Du ein sauberes 'Repository' willst, ist es am besten, einen neuen Klon anzulegen. Sei auch vorsichtig, wenn Du +.git+ direkt manipulierst: was, wenn zeitgleich ein Git Kommando ausgeführt wird oder plötzlich der Strom ausfällt? Generell sollten Referenzen mit *git update-ref -d* gelöscht werden, auch wenn es gewöhnlich sicher ist +refs/original+ von Hand zu löschen. === 'Commits' === Wir haben nun zwei von drei Objekten erklärt. Das dritte ist ein 'Commit'-Objekt. Sein Inhalt hängt von der 'Commit'-Beschreibung ab, wie auch vom Zeitpunkt der Erstellung. Damit alles zu unserem Beispiel passt, müssen wir ein wenig tricksen: $ git commit --amend -m Shakespeare # Ändere die Bemerkung. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Manipuliere Zeitstempel und Autor. $ find .git/objects -type f Du solltest nun +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ finden, was dem SHA1-Hash-Wert seines Inhalts entspricht: "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice 1234567890 -0800" LF "committer Bob 1234567890 -0800" LF LF "Shakespeare" LF Wie vorhin kannst Du 'zpipe' oder 'cat-file' benutzen, um es für Dich zu überprüfen. Das ist der erste 'Commit' gewesen, deshalb gibt es keine Eltern-'Commits'. Aber spätere 'Commits' werden immer mindestens eine Zeile enthalten, die den Eltern-'Commit' identifiziert. === Von Magie nicht zu unterscheiden === Git's Geheimnisse scheinen zu einfach. Es sieht so aus, als müsste man nur ein paar Kommandozeilenskripte zusammenmixen, einen Schuß C-Code hinzufügen und innerhalb ein paar Stunden ist man fertig: eine Mischung von grundlegenden Dateisystemoperationen und SHA1-Hash-Berechnungen, garniert mit Sperrdateien und Synchronisation für Stabilität. Tatsächlich beschreibt dies die früheste Version von Git. Nichtsdestotrotz, abgesehen von geschickten Verpackungstricks, um Speicherplatz zu sparen, und geschickten Indizierungstricks, um Zeit zu sparen, wissen wir nun, wie Git gewandt ein Dateisystem in eine Datenbank verwandelt, das perfekt für eine Versionsverwaltung geeignet ist. Angenommen, wenn irgendeine Datei in der Objektdatenbank durch einen Laufwerksfehler zerstört wird, dann wird sein SHA1-Hash-Wert nicht mehr mit seinem Inhalt übereinstimmen und uns sagen, wo das Problem liegt. Durch Bilden von SHA1-Hash-Werten aus den SHA1-Hash-Werten anderer Objekte, erreichen wir Integrität auf allen Ebenen. 'Commits' sind elementar, das heißt, ein 'Commit' kann niemals nur Teile einer Änderung speichern: wir können den SHA1-Hash-Wert eines 'Commits' erst dann berechnen und speichern, nachdem wir bereits alle relevanten 'Tree'-Objekte, 'Blob'-Objekte und Eltern-'Commits' gespeichert haben. Die Objektdatenbank ist immun gegen unerwartete Unterbrechungen wie zum Beispiel einen Stromausfall. Wir können sogar den hinterhältigsten Gegnern widerstehen. Stell Dir vor, jemand will den Inhalt einer Datei ändern, die in einer älteren Version eines Projekt liegt. Um die Objektdatenbank intakt aussehen zu lassen, müssten sie außerdem den SHA1-Hash-Wert des korrespondierenden 'Blob'-Objekt ändern, da die Datei nun eine geänderte Zeichenfolge enthält. Das heißt auch, dass sie jeden SHA1-Hash-Wert der 'Tree'-Objekte ändern müssen, welche dieses Objekt referenzieren und demzufolge alle SHA1-Hash-Werte der 'Commit'-Objekte, welche diese 'Tree'-Objekte beinhalten, zusätzlich zu allen Abkömmlingen dieses 'Commits'. Das bedeutet auch, dass sich der SHA1-Hash-Wert des offiziellen HEAD von dem des manipulierten 'Repository' unterscheidet. Folgen wir dem Pfad der differierenden SHA1-Hash-Werte, finden wir die verstümmelte Datei, wie auch den 'Commit', in dem sie erstmals auftauchte. Kurz gesagt, so lange die 20 Byte, welche den SHA1-Hash-Wert des letzen 'Commit' repräsentieren sicher sind, ist es unmöglich ein Git 'Repository' zu fälschen. Was ist mit Git's berühmten Fähigkeiten? 'Branching'? 'Merging'? 'Tags'? Nur Kleinigkeiten. Der aktuelle HEAD wird in der Datei +.git/HEAD+ gehalten, welche den SHA1-Hash-Wert eines 'Commit'-Objekts enthält. Der SHA1-Hash-Wert wird während eines 'Commit' aktualisiert, genauso bei vielen anderen Anweisungen. 'Branches' sind fast das selbe: sie sind Dateien in +.git/refs/heads+. 'Tags' ebenso: sie stehen in +.git/refs/tags+ aber sie werden durch einen Satz anderer Anweisungen aktualisiert. gitmagic-20160304/de/multiplayer.txt0000644000175000017500000002427712666307504016514 0ustar sbadiasbadia== Multiplayer Git == Anfangs benutzte ich Git bei einem privaten Projekt, bei dem ich der einzige Entwickler war. Unter den Befehlen im Zusammenhang mit Git's verteilter Art, brauchte ich nur *pull* und *clone*, damit konnte ich das selbe Projekt an unterschiedlichen Orten halten. Später wollte ich meinen Code mit Git veröffentlichen und Änderungen von Mitstreitern einbinden. Ich musste lernen, wie man Projekte verwaltet, an denen mehrere Entwickler aus aller Welt beteiligt waren. Glücklicherweise ist das Git's Stärke und wohl auch seine Daseinsberechtigung. === Wer bin ich? === Jeder 'Commit' enthält Name und eMail-Adresse des Autors, welche mit *git log* angezeigt werden. Standardmäßig nutzt Git Systemeinstellungen, um diese Felder auszufüllen. Um diese Angaben explizit zu setzen, gib ein: $ git config --global user.name "Max Mustermann" $ git config --global user.email maxmustermann@beispiel.de Lasse den -global Schalter weg, um diese Einstellungen für das aktuelle 'Repository' zu setzen. === Git über SSH, HTTP === Angenommen, Du hast einen SSH-Zugang zu einem Webserver, aber Git ist nicht installiert. Wenn auch nicht so effizient wie mit dem systemeigenen Protokoll, kann Git über HTTP kommunizieren. Lade Git herunter, compiliere und installiere es unter Deinem Benutzerkonto und erstellen ein 'Repository' in Deinem Webverzeichnis: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update Bei älteren Git Versionen funktioniert der 'copy'-Befehl nicht, stattdessen gib ein: $ chmod a+x hooks/post-update Nun kannst Du Deine letzten Änderungen über SSH von jedem 'Clone' aus veröffentlichen. $ git push web.server:/pfad/zu/proj.git master und jedermann kann Dein Projekt abrufen mit: $ git clone http://web.server/proj.git === Git über alles === Willst Du 'Repositories' ohne Server synchronisieren oder gar ohne Netzwerkverbindung? Musst Du während eines Notfalls improvisieren? Wir haben gesehen, dass man mit <>. Wir können solche Dateien hin und her schicken, um Git 'Repositories' über jedes beliebige Medium zu transportieren, aber ein effizienteres Werkzeug ist *git bundle*. Der Absender erstellt ein 'Bundle': $ git bundle create einedatei HEAD und transportiert das 'Bundle' +einedatei+ irgendwie zum anderen Beteiligten: per eMail, USB-Stick, einen *xxd* Hexdump und einen OCR Scanner, Morsecode über Telefon, Rauchzeichen usw. Der Empfänger holt sich die 'Commits' aus dem 'Bundle' durch Eingabe von: $ git pull einedatei Der Empfänger kann das sogar mit einem leeren 'Repository' tun. Trotz seiner Größe, +einedatei+ enthält das komplette original Git 'Repository'. In größeren Projekten vermeidest Du Datenmüll, indem Du nur Änderungen 'bundlest', die in den anderen 'Repositories' fehlen. Zum Beispiel, nehmen wir an, der 'Commit' ``1b6d...'' ist der aktuellste, den beide Parteien haben: $ git bundle create einedatei HEAD ^1b6d Macht man das regelmäßig, kann man leicht vergessen, welcher 'Commit' zuletzt gesendet wurde. Die Hilfeseiten schlagen vor, 'Tags' zu benutzen, um dieses Problem zu lösen. Das heißt, nachdem Du ein 'Bundle' gesendet hast, gib ein: $ git tag -f letztesbundle HEAD und erstelle neue Aktualisierungsbundles mit: $ git bundle create neuesbundle HEAD ^letztesbundle === Patches: Das globale Zahlungsmittel === 'Patches' sind die Klartextdarstellung Deiner Änderungen, die von Computern und Menschen gleichermaßen einfach verstanden werden. Dies verleiht ihnen eine universelle Anziehungskraft. Du kannst einen 'Patch' Entwicklern schicken, ganz egal, was für ein Versionsverwaltungssystem sie benutzen. Solange Deine Mitstreiter ihre eMails lesen können, können sie auch Deine Änderungen sehen. Auch auf Deiner Seite ist alles was Du brauchst ein eMail-Konto: es gibt keine Notwendigkeit ein Online Git 'Repository' aufzusetzen. Erinnere Dich an das erste Kapitel: $ git diff 1b6d > mein.patch gibt einen 'Patch' aus, der zur Diskussion einfach in eine eMail eingefügt werden kann. In einem Git 'Repository' gib ein: $ git apply < mein.patch um den 'Patch' anzuwenden. In einer offizielleren Umgebung, wenn Autorennamen und eventuell Signaturen aufgezeichnet werden sollen, erstelle die entsprechenden 'Patches' nach einem bestimmten Punkt durch Eingabe von: $ git format-patch 1b6d Die resultierenden Dateien können an *git-send-email* übergeben werden oder von Hand verschickt werden. Du kannst auch eine Gruppe von 'Commits' angeben: $ git format-patch 1b6d..HEAD^^ Auf der Empfängerseite speichere die eMail in eine Datei, dann gib ein: $ git am < email.txt Das wendet den eingegangenen 'Patch' an und erzeugt einen 'Commit', inklusive der Informationen wie z.B. den Autor. Mit einer Webmail Anwendung musst Du eventuell ein Button anklicken, um die eMail in ihrem rohen Originalformat anzuzeigen, bevor Du den 'Patch' in eine Datei sicherst. Es gibt geringfügige Unterschiede bei mbox-basierten eMail Anwendungen, aber wenn Du eine davon benutzt, gehörst Du vermutlich zu der Gruppe Personen, die damit einfach umgehen können, ohne Anleitungen zu lesen.! === Entschuldigung, wir sind umgezogen. === Nach dem 'Clonen' eines 'Repositories', wird *git push* oder *git pull* automatisch auf die original URL zugreifen. Wie macht Git das? Das Geheimnis liegt in der Konfiguration, die beim 'Clonen' erzeugt wurde. Lasst uns einen Blick riskieren: $ git config --list Die +remote.origin.url+ Option kontrolliert die Quell-URL; ``origin'' ist der Spitzname, der dem Quell-'Repository' gegeben wurde. Wie mit der ``master'' 'Branch' Konvention können wir diesen Spitznamen ändern oder löschen, aber es gibt für gewöhnlich keinen Grund, dies zu tun. Wenn das original 'Repository' verschoben wird, können wir die URL aktualisieren mit: $ git config remote.origin.url git://neue.url/proj.git Die +branch.master.merge+ Option definiert den Standard-Remote-'Branch' bei einem *git pull*. Während des ursprünglichen 'Clonen' wird sie auf den aktuellen 'Branch' des Quell-'Repository' gesetzt, so dass selbst dann, wenn der 'HEAD' des Quell-'Repository' inzwischen auf einen anderen 'Branch' gewechselt hat, ein späterer 'pull' treu dem original 'Branch' folgen wird. Diese Option gilt nur für das 'Repository', von dem als erstes 'gecloned' wurde, was in der Option +branch.master.remote+ hinterlegt ist. Bei einem 'pull' aus anderen 'Repositories' müssen wir explizit angeben, welchen 'Branch' wir wollen: $ git pull git://beispiel.com/anderes.git master Das obige erklärt, warum einige von unseren früheren 'push' und 'pull' Beispielen keine Argumente hatten. === Entfernte 'Branches' === Wenn Du ein 'Repository' 'clonst', 'clonst' Du auch alle seine 'Branches'. Das hast Du vielleicht noch nicht bemerkt, denn Git versteckt diese: Du musst speziell danach fragen. Das verhindert, dass 'Branches' vom entfernten 'Repository' Deine lokalen 'Branches' stören, und es macht Git einfacher für Anfänger. Zeige die entfernten 'Branches' an mit: $ git branch -r Du solltes etwas sehen wie: origin/HEAD origin/master origin/experimentell Diese Liste zeigt die 'Branches' und den HEAD des entfernten 'Repository', welche auch in regulären Git Anweisungen verwendet werden können. Zum Beispiel, angenommen Du hast viele 'Commits' gemacht und möchtest einen Vergleich zur letzten abgeholten Version machen. Du kannst die Logs nach dem entsprechenden SHA1 Hashwert durchsuchen, aber es ist viel einfacher, folgendes einzugeben: $ git diff origin/HEAD Oder Du kannst schauen, was auf dem 'Branch' ``experimentell'' los war: $ git log origin/experimentell === Mehrere 'Remotes' === Angenommen, zwei andere Entwickler arbeiten an Deinem Projekt, und wir wollen beide im Auge behalten. Wir können mehr als ein 'Repository' gleichzeitig beobachten mit: $ git remote add other git://example.com/some_repo.git $ git pull other some_branch Nun haben wir einen 'Branch' vom zweiten 'Repository' eingebunden und wir haben einfachen Zugriff auf alle 'Branches' von allen 'Repositories': $ git diff origin/experimentell^ other/some_branch~5 Aber was, wenn wir nur deren Änderungen vergleichen wollen, ohne unsere eigene Arbeit zu beeinflussen? Mit anderen Worten, wir wollen ihre 'Branches' untersuchen, ohne dass deren Änderungen in unser Arbeitsverzeichnis einfließen. Anstatt 'pull' benutzt Du dann: $ git fetch # Fetch vom origin, der Standard. $ git fetch other # Fetch vom zweiten Programmierer. Dies holt lediglich die Chroniken. Obwohl das Arbeitsverzeichnis unverändert bleibt, können wir nun jeden 'Branch' aus jedem 'Repository' in einer Git Anweisung referenzieren, da wir eine lokale Kopie besitzen. Erinnere Dich, dass ein 'Pull' hinter den Kulissen einfach ein *fetch* gefolgt von einem *merge* ist. Normalerweise machen wir einen *pull*, weil wir die letzten 'Commits' abrufen und einbinden wollen. Die beschriebene Situation ist eine erwähnenswerte Ausnahme. Siehe *git help remote*, um zu sehen, wie man Remote-'Repositories' entfernt, bestimmte 'Branches' ignoriert und mehr. === Meine Einstellungen === Für meine Projekte bevorzuge ich es, wenn Unterstützer 'Repositories' vorbereiten, von denen ich 'pullen' kann. Verschiedene Git Hosting Anbieter lassen Dich mit einem Klick deine eigene 'Fork' eines Projekts hosten. Nachdem ich einen Zweig abgerufen habe, benutze ich Git Anweisungen, um durch die Änderungen zu navigieren und zu untersuchen, die idealerweise gut organisiert und dokumentiert sind. Ich 'merge' meine eigenen Änderungen und führe eventuell weitere Änderungen durch. Wenn ich zufrieden bin, 'pushe' ich in das zentrale 'Repository'. Obwohl ich nur unregelmäßig Beiträge erhalte, glaube ich, dass diese Methode sich auszahlt. Siehe http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[diesen Blog Beitrag von Linus Torvalds (englisch)]. In der Git Welt zu bleiben, ist etwas bequemer als 'Patch'-Dateien, denn es erspart mir, sie in Git 'Commits' zu konvertieren. Außerdem kümmert sich Git um die Details wie Autorname und eMail-Adresse, genauso wie um Datum und Uhrzeit und es fordert den Autor zum Beschreiben seiner eigenen Änderungen auf. gitmagic-20160304/de/Makefile0000644000175000017500000000002712666307504015027 0ustar sbadiasbadia../po4gitmagic/Makefilegitmagic-20160304/de/preface.txt0000644000175000017500000001073512666307504015544 0ustar sbadiasbadia= Git Magic = Ben Lynn August 2007 == Vorwort == http://git.or.cz/[Git] ist eine Art "Schweizer Taschenmesser" für Versionsverwaltung. Ein zuverlässiges, vielseitiges Mehrzweck-Versionsverwaltungswerkzeug, dessen außergewöhnliche Flexibilität es schwierig zu erlernen macht, ganz zu schweigen davon, es zu meistern. Wie Arthur C. Clarke festgestellt hat, ist jede hinreichend fortschrittliche Technologie nicht von Magie zu unterscheiden. Das ist ein großartiger Ansatz, um an Git heranzugehen: Anfänger können seine inneren Mechanismen ignorieren und Git als ein Ding ansehen, dass mit seinen erstaunlichen Fähigkeiten Freunde verzückt und Gegner zur Weißglut bringt. Anstatt die Details aufzudecken, bieten wir grobe Anweisungen für die jeweiligen Funktionen. Bei regelmäßiger Anwendung wirst Du allmählich verstehen, wie die Tricks funktionieren und wie Du die Rezepte auf Deinen Bedarf zuschneiden kannst. .Übersetzungen - link:/\~blynn/gitmagic/intl/zh_cn/[Vereinfachtes Chinesisch]: von JunJie, Meng und JiangWei. Zu link:/~blynn/gitmagic/intl/zh_tw/[Traditionellem Chinesisch] konvertiert via +cconv -f UTF8-CN -t UTF8-TW+. - link:/~blynn/gitmagic/intl/fr/[Französich]: von Alexandre Garel, Paul Gaborit, und Nicolas Deram. Auch gehostet unter http://tutoriels.itaapy.com/[itaapy]. - link:/~blynn/gitmagic/intl/de/[Deutsch]: von Benjamin Bellee und Armin Stebich; Auch gehostet unter http://gitmagic.lordofbikes.de/[Armin's Website]. - link:/~blynn/gitmagic/intl/it/[Italienisch]: von Mattia Rigotti. - http://www.slideshare.net/slide_user/magia-git[Portugiesisch]: von Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[ODT-Version]]. - link:/~blynn/gitmagic/intl/ru/[Russisch]: von Tikhon Tarnavsky, Mikhail Dymskov, und anderen. - link:/~blynn/gitmagic/intl/es/[Spanisch]: von Rodrigo Toledo und Ariset Llerena Tapia. - link:/~blynn/gitmagic/intl/vi/[Vietnamesisch]: von Trần Ngọc Quân; Auch gehostet unter http://vnwildman.users.sourceforge.net/gitmagic.html[seiner Website]. .Andere Ausgaben - link:book.html[Einzelne Webseite]: reines HTML, ohne CSS. - link:book.pdf[PDF Datei]: druckerfreundlich. - http://packages.debian.org/gitmagic[Debian Packet], http:://packages.ubuntu.com/gitmagic[Ubuntu Packet]: Für eine schnelle und lokale Kopie dieser Seite. Praktisch, http://csdcf.stanford.edu/status/[wenn dieser Server offline ist]. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Gedrucktes Buch [Amazon.com]]: 64 Seiten, 15.24cm x 22.86cm, schwarz/weiß. Praktisch, wenn es keinen Strom gibt. === Danke! === Ich bin erstaunt, dass so viele Leute an der Übersetzung dieser Seiten gearbeitet haben. Ich weiß es zu würdigen, dass ich, dank der Bemühungen der oben genannten, einen größeren Leserkreis erreiche. Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, und Tyler Breisacher haben Korrekturen und Verbesserungen beigesteuert. François Marier unterhält das Debian Packet, das ursprünglich von Daniel Baumann erstellt wurde. Meine Dankbarkeit gilt auch vielen anderen für deren Unterstützung und Lob. Ich war versucht, Euch hier alle aufzuzählen, aber das könnte Erwartungen in unermesslichem Umfang wecken. Wenn ich Dich versehentlich vergessen habe, sag' mir bitte Bescheid oder schicke mir einfach einen Patch! .Kostenloses Git Hosting - http://repo.or.cz/[http://repo.or.cz/] hostet freie Projekte. Die allererste Git Hosting Seite. Gegründet und betrieben von einem der ersten Git Entwickler. - http://gitorious.org/[http://gitorious.org/] ist eine andere Git Hosting Seite, bevorzugt für Open-Source Projekte. - http://github.com/[http://github.com/] hostet Open-Source Projekte kostenlos und geschlossene Projekte gegen Gebühr. Vielen Dank an alle diese Seiten für das Hosten dieser Anleitung. === Lizenz === Diese Anleitung ist unter der http://www.gnu.org/licenses/gpl-3.0.html[GNU General Public License Version 3] veröffentlicht. Natürlich wird der Quelltext in einem Git 'Repository' gehalten und kann abgerufen werden durch: $ git clone git://repo.or.cz/gitmagic.git # Erstellt "gitmagic" Verzeichnis. oder von einem der Mirrorserver: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git gitmagic-20160304/de/basic.txt0000644000175000017500000002014412666307504015213 0ustar sbadiasbadia== Erste Schritte == Bevor wir uns in ein Meer von Git-Befehlen stürzen, schauen wir uns ein paar einfache Beispiele an. Trotz ihrer Einfachheit sind alle davon wichtig und nützlich. Um ehrlich zu sein, in meinen ersten Monaten mit Git brauchte ich nicht mehr, als in diesem Kapitel beschrieben steht. === Stand sichern === Hast Du gravierende Änderungen vor? Nur zu, aber speichere Deinen aktuellen Stand vorher lieber nochmal ab: $ git init $ git add . $ git commit -m "Meine erste Sicherung" Falls deine Änderungen schief gehen, kannst du jetzt die alte Version wiederherstellen: $ git reset --hard Um den neuen Stand zu sichern: $ git commit -a -m "Eine andere Sicherung" === Hinzufügen, Löschen, Umbenennen === Bisher kümmert sich Git nur um Dateien, die existierten, als Du das erste Mal *git add* ausgeführt hast. Wenn Du Dateien oder Verzeichnisse hinzufügst, musst du Git das mitteilen: $ git add readme.txt Dokumentation Ebenso, wenn Git Dateien vergessen soll: $ git rm ramsch.h veraltet.c $ git rm -r belastendes/material/ Git löscht diese Dateien für Dich, falls Du es noch nicht getan hast. Eine Datei umzubenennen, ist das selbe, wie sie zu löschen und unter neuem Namen hinzuzufügen. Git benutzt hierzu die Abkürzung *git mv*, welche die gleiche Syntax wie *mv* hat. Zum Beispiel: $ git mv fehler.c feature.c === Fortgeschrittenes Rückgängig machen/Wiederherstellen === Manchmal möchtest Du einfach zurückgehen und alle Änderungen ab einem bestimmten Zeitpunkt verwerfen, weil sie falsch waren. Dann: $ git log zeigt Dir eine Liste der bisherigen 'Commits' und deren SHA1 Hashwerte: ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob Date: Tue Mar 14 01:59:26 2000 -0800 Ersetze printf() mit write(). commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alice Date: Thu Jan 1 00:00:00 1970 +0000 Initial commit. ---------------------------------- Die ersten paar Zeichen eines Hashwert reichen aus, um einen 'Commit' zu identifizieren; alternativ benutze kopieren und einfügen für den kompletten Hashwert. Gib ein: $ git reset --hard 766f um den Stand eines bestimmten 'Commits' wieder herzustellen und alle nachfolgenden Änderungen für immer zu löschen. Ein anderes Mal willst Du nur kurz zu einem älteren Stand springen. In diesem Fall, gib folgendes ein: $ git checkout 82f5 Damit springst Du in der Zeit zurück, behältst aber neuere Änderungen. Aber, wie bei Zeitreisen in einem Science-Fiction-Film, wenn Du jetzt etwas änderst und 'commitest', gelangst Du in eine alternative Realität, denn Deine Änderungen sind anders als beim früheren 'Commit'. Diese alternative Realität heißt 'Branch', und <>. Für jetzt merke Dir, $ git checkout master bringt Dich wieder in die Gegenwart. Um zu verhindern, dass sich Git beschwert, solltest Du vor einem 'Checkout' alle Änderungen 'commiten' oder 'reseten'. Um wieder die Computerspielanalogie anzuwenden: - *`git reset --hard`*: Lade einen alten Stand und lösche alle Spielstände, die neuer sind als der jetzt geladene. - *`git checkout`*: Lade einen alten Spielstand, aber wenn Du weiterspielst, wird der Spielstand von den früher gesicherten Spielständen abweichen. Jeder Spielstand, der ab jetzt gesichert wird, entsteht in dem separaten 'Branch', welcher der alternative Realität entspricht. <>. Du kannst auch nur einzelne Dateien oder Verzeichnisse wiederherstellen, indem Du sie an den Befehl anhängst: $ git checkout 82f5 eine.datei andere.datei Sei vorsichtig, diese Art des '*Checkout*' kann Dateien überschreiben, ohne dass Du etwas merkst. Um Unfälle zu vermeiden, solltest Du immer 'commiten' bevor Du ein 'Checkout' machst, besonders am Anfang, wenn Du Git noch erlernst. Allgemein gilt: Wenn Du unsicher bist, egal, ob ein Git Befehl oder irgendeine andere Operation, führe zuerst *git commit -a* aus. Du magst Kopieren und Einfügen von Hashes nicht? Dann nutze: $ git checkout :/"Meine erste Si" um zu einem 'Commit' zu springen, dessen Beschreibung so anfängt. Du kannst auch nach dem 5. letzten 'Commit' fragen: $ git checkout master~5 === Rückgängig machen === In einem Gerichtssaal können Ereignisse aus den Akten gelöscht werden. Ähnlich kannst Du gezielt 'Commits' rückgängig machen. $ git commit -a $ git revert 1b6d wird den 'Commit' mit dem angegebenen Hashwert rückgängig machen. Das Rückgängig machen wird als neuer 'Commit' erstellt, was mit *git log* überprüft werden kann. === Changelog erstellen === Verschiedene Projekte benötigen ein http://de.wikipedia.org/wiki/%C3%84nderungsprotokoll[Änderungsprotokoll]. Das kannst du mit folgendem Befehl erstellen: $ git log > ChangeLog === Dateien herunterladen === Eine Kopie eines mit Git verwalteten Projekts bekommst du mit: $ git clone git://server/pfad/zu/dateien Um zum Beispiel alle Dateien zu bekommen, die ich zum Erzeugen dieser Seiten benutze: $ git clone git://git.or.cz/gitmagic.git Es gibt gleich noch viel mehr über den 'clone' Befehl zu sagen. === Das Neueste vom Neuen === Wenn Du schon eine Kopie eines Projektes hast, kannst Du es auf die neuste Version aktualisieren mit: $ git pull === Einfaches Veröffentlichen === Angenommen Du hast ein Skript geschrieben und möchtest es anderen zugänglich machen. Du könntest sie einfach bitten, es von Deinem Computer herunterzuladen, aber falls sie das tun, während Du experimentierst oder das Skript verbesserst, könnten sie in Schwierigkeiten geraten. Genau deswegen gibt es Releasezyklen. Entwickler arbeiten regelmäßig an einem Projekt, veröffentlichen den Code aber nur, wenn sie ihn für vorzeigbar halten. Um dies in Git zu tun, gehe ins Verzeichnis in dem das Skript liegt: $ git init $ git add . $ git commit -m "Erster Stand" Dann sage Deinen Nutzern: $ git clone dein.computer:/pfad/zum/skript um Dein Skript herunterzuladen. Das setzt voraus, dass sie einen SSH-Zugang haben. Falls nicht, führe *git deamon* aus und sage den Nutzern folgendes: $ git clone git://dein.computer/pfad/zum/skript Ab jetzt, immer wenn Dein Skript reif für eine Veröffentlichung ist: $ git commit -a -m "Nächster Stand" und Deine Nutzer können ihr Skript aktualisieren mit: $ git pull Deine Nutzer werden nie mit Versionen in Kontakt kommen, von denen Du es nicht willst. Natürlich funktioniert der Trick für fast alles, nicht nur Skripts. === Was habe ich getan? === Finde heraus, was Du seit dem letzten 'Commit' getan hast: $ git diff Oder seit Gestern: $ git diff "@{gestern}" Oder zwischen irgendeiner Version und der vorvorletzten: $ git diff 1b6d "master~2" Jedes mal ist die Ausgabe ein 'Patch', der mit *git apply* eingespielt werden kann. Versuche auch: $ git whatchanged --since="2 weeks ago" Um mir die Geschichte eines 'Repositories' anzuzeigen, benutze ich häufig http://sourceforge.net/projects/qgit[qgit] da es eine schicke Benutzeroberfläche hat, oder http://jonas.nitro.dk/tig/[tig], eine Konsolenanwendung, die sehr gut über langsame Verbindungen funktioniert. Alternativ kannst Du einen Webserver installieren mit *git instaweb*, dann kannst Du mit jedem Webbrowser darauf zugreifen. === Übung === A, B, C, D sind 4 aufeinander folgende 'Commits'. B ist identisch mit A, außer, dass einige Dateien gelöscht wurden. Wir möchten die Dateien in D wieder hinzufügen, aber nicht in B. Wie machen wir das? Es gibt mindestens 3 Lösungen. Angenommen, wir sind bei D: 1. Der Unterschied zwischen A und B sind die gelöschten Dateien. Wir können einen 'Patch' erstellen, der diesen Unterschied darstellt und diesen dann auf D anwenden: $ git diff B A | git apply 2. Da die Dateien im 'Repository' unter dem 'Commit' A gespeichert sind, können wir sie wieder herstellen: $ git checkout A foo.c bar.h 3. Wir können den 'Commit' von A auf B als Änderung betrachten, die wir rückgängig machen wollen: $ git revert B Welche Lösung ist die beste? Die, welche Dir am besten gefällt. Es ist einfach, mit Git das zu bekommen, was Du willst und oft führen viele Wege zum Ziel. gitmagic-20160304/de/pot/0000755000175000017500000000000012666307504014172 5ustar sbadiasbadiagitmagic-20160304/de/pot/multiplayer.po0000644000175000017500000006311112666307504017103 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010, 2011. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: bennlynn@gmail.com\n" "POT-Creation-Date: 2010-11-05 17:51+0100\n" "PO-Revision-Date: 2011-01-05 08:43+0100\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/multiplayer.txt:2 msgid "== Multiplayer Git ==" msgstr "== Multiplayer Git ==" #. type: Plain text #: ../en/multiplayer.txt:6 msgid "" "Initially I used Git on a private project where I was the sole developer. " "Amongst the commands related to Git's distributed nature, I needed only " "*pull* and *clone* so could I keep the same project in different places." msgstr "" "Anfangs benutzte ich Git bei einem privaten Projekt, bei dem ich der einzige " "Entwickler war. Unter den Befehlen im Zusammenhang mit Git's verteilter Art, " "brauchte ich nur *pull* und *clone*, damit konnte ich das selbe Projekt an " "unterschiedlichen Orten halten." #. type: Plain text #: ../en/multiplayer.txt:11 msgid "" "Later I wanted to publish my code with Git, and include changes from " "contributors. I had to learn how to manage projects with multiple developers " "from all over the world. Fortunately, this is Git's forte, and arguably its " "raison d'être." msgstr "" "Später wollte ich meinen Code mit Git veröffentlichen und Änderungen von " "Mitstreitern einbinden. Ich musste lernen, wie man Projekte verwaltet, an " "denen mehrere Entwickler aus aller Welt beteiligt waren. Glücklicherweise " "ist das Git's Stärke und wohl auch seine Daseinsberechtigung." #. type: Plain text #: ../en/multiplayer.txt:13 msgid "=== Who Am I? ===" msgstr "=== Wer bin ich? ===" #. type: Plain text #: ../en/multiplayer.txt:17 msgid "" "Every commit has an author name and email, which is shown by *git log*. By " "default, Git uses system settings to populate these fields. To set them " "explicitly, type:" msgstr "" "Jeder 'Commit' enthält Name und eMail-Adresse des Autors, welche mit *git " "log* angezeigt werden. Standardmäßig nutzt Git Systemeinstellungen um diese " "Felder auszufüllen. Um diese Angaben explizit zu setzen, gib ein:" #. type: Plain text #: ../en/multiplayer.txt:20 #, no-wrap msgid "" " $ git config --global user.name \"John Doe\"\n" " $ git config --global user.email johndoe@example.com\n" msgstr "" " $ git config --global user.name \"Max Mustermann\"\n" " $ git config --global user.email maxmustermann@beispiel.de\n" #. type: Plain text #: ../en/multiplayer.txt:22 msgid "" "Omit the global flag to set these options only for the current repository." msgstr "" "Lasse den -global Schalter weg um diese Einstellungen für das aktuelle " "'Repository' zu setzen." #. type: Plain text #: ../en/multiplayer.txt:24 msgid "=== Git Over SSH, HTTP ===" msgstr "=== Git über SSH, HTTP ===" #. type: Plain text #: ../en/multiplayer.txt:27 msgid "" "Suppose you have SSH access to a web server, but Git is not installed. " "Though less efficient than its native protocol, Git can communicate over " "HTTP." msgstr "" "Angenommen, Du hast einen SSH-Zugang zu einem Webserver aber Git ist nicht " "installiert. Wenn auch nicht so effizient wie mit dem systemeigenen " "Protokoll, kann Git über HTTP kommunizieren." #. type: Plain text #: ../en/multiplayer.txt:30 msgid "" "Download, compile and install Git in your account, and create a repository " "in your web directory:" msgstr "" "Lade Git herunter, compiliere und installiere es unter Deinem Benutzerkonto " "und erstellen ein 'Repository' in Deinem Webverzeichnis:" #. type: Plain text #: ../en/multiplayer.txt:35 #, no-wrap msgid "" " $ GIT_DIR=proj.git git init\n" " $ cd proj.git\n" " $ git --bare update-server-info\n" " $ cp hooks/post-update.sample hooks/post-update\n" msgstr "" " $ GIT_DIR=proj.git git init\n" " $ cd proj.git\n" " $ git --bare update-server-info\n" " $ cp hooks/post-update.sample hooks/post-update\n" #. type: Plain text #: ../en/multiplayer.txt:37 msgid "For older versions of Git, the copy command fails and you should run:" msgstr "" "Bei älteren Git Versionen funktioniert der 'copy'-Befehl nicht, stattdessen " "gib ein:" #. type: Plain text #: ../en/multiplayer.txt:39 #, no-wrap msgid " $ chmod a+x hooks/post-update\n" msgstr " $ chmod a+x hooks/post-update\n" #. type: Plain text #: ../en/multiplayer.txt:41 msgid "Now you can publish your latest edits via SSH from any clone:" msgstr "" "Nun kannst Du Deine letzten Änderungen über SSH von jedem 'Clone' aus " "veröffentlichen." #. type: Plain text #: ../en/multiplayer.txt:43 #, no-wrap msgid " $ git push web.server:/path/to/proj.git master\n" msgstr " $ git push web.server:/pfad/zu/proj.git master\n" #. type: Plain text #: ../en/multiplayer.txt:45 msgid "and anybody can get your project with:" msgstr "und jedermann kann Dein Projekt abrufen mit:" #. type: Plain text #: ../en/multiplayer.txt:47 #, no-wrap msgid " $ git clone http://web.server/proj.git\n" msgstr " $ git clone http://web.server/proj.git\n" #. type: Plain text #: ../en/multiplayer.txt:49 msgid "=== Git Over Anything ===" msgstr "=== Git über alles ===" #. type: Plain text #: ../en/multiplayer.txt:55 msgid "" "Want to synchronize repositories without servers, or even a network " "connection? Need to improvise during an emergency? We've seen " "<>. We could shuttle such files back " "and forth to transport git repositories over any medium, but a more " "efficient tool is *git bundle*." msgstr "" "Willst Du 'Repositories' ohne Server synchronisieren oder gar ohne " "Netzwerkverbindung? Musst Du während eines Notfalls improvisieren? Wir haben " "gesehen, dass man mit <>. " "Wir können solche Dateien hin und her schicken um Git 'Repositories' über " "jedes beliebige Medium zu transportieren, aber ein effizienteres Werkzeug " "ist *git bundle*." #. type: Plain text #: ../en/multiplayer.txt:57 msgid "The sender creates a 'bundle':" msgstr "Der Absender erstellt ein 'Bundle':" #. type: Plain text #: ../en/multiplayer.txt:59 #, no-wrap msgid " $ git bundle create somefile HEAD\n" msgstr " $ git bundle create einedatei HEAD\n" #. type: Plain text #: ../en/multiplayer.txt:63 msgid "" "then transports the bundle, +somefile+, to the other party somehow: email, " "thumb drive, an *xxd* printout and an OCR scanner, reading bits over the " "phone, smoke signals, etc. The receiver retrieves commits from the bundle by " "typing:" msgstr "" "und transportiert das 'Bundle' +einedatei+ irgendwie zum anderen " "Beteiligten: per eMail, USB-Stick, einen *xxd* Hexdump und einen OCR " "Scanner, Morsecode über Telefon, Rauchzeichen usw. Der Empfänger holt sich " "die 'Commits' aus dem 'Bundle' durch Eingabe von:" #. type: Plain text #: ../en/multiplayer.txt:65 #, no-wrap msgid " $ git pull somefile\n" msgstr " $ git pull einedatei\n" #. type: Plain text #: ../en/multiplayer.txt:68 msgid "" "The receiver can even do this from an empty repository. Despite its size, " "+somefile+ contains the entire original git repository." msgstr "" "Der Empfänger kann das sogar mit einem leeren 'Repository' tun. Trotz seiner " "Größe, +einedatei+ enthält das komplette original Git 'Repository'." #. type: Plain text #: ../en/multiplayer.txt:72 msgid "" "In larger projects, eliminate waste by bundling only changes the other " "repository lacks. For example, suppose the commit ``1b6d...'' is the most " "recent commit shared by both parties:" msgstr "" "In größeren Projekten, vermeidest Du Datenmüll indem Du nur Änderungen " "'bundlest', die in den anderen 'Repositories' fehlen. Zum Beispiel, nehmen " "wir an, der 'Commit' ``1b6d...'' ist der aktuellste, den beide Parteien " "haben:" #. type: Plain text #: ../en/multiplayer.txt:74 #, no-wrap msgid " $ git bundle create somefile HEAD ^1b6d\n" msgstr " $ git bundle create einedatei HEAD ^1b6d\n" #. type: Plain text #: ../en/multiplayer.txt:78 msgid "" "If done frequently, one could easily forget which commit was last sent. The " "help page suggests using tags to solve this. Namely, after you send a " "bundle, type:" msgstr "" "Macht man das regelmäßig, kann man leicht vergessen, welcher 'Commit' " "zuletzt gesendet wurde. Die Hilfeseiten schlagen vor 'Tags' zu benutzen um " "dieses Problem zu lösen. Das heißt, nachdem Du ein 'Bundle' gesendet hast, " "gib ein:" #. type: Plain text #: ../en/multiplayer.txt:80 #, no-wrap msgid " $ git tag -f lastbundle HEAD\n" msgstr " $ git tag -f letztesbundle HEAD\n" #. type: Plain text #: ../en/multiplayer.txt:82 msgid "and create new refresher bundles with:" msgstr "und erstelle neue Aktualisierungsbundles mit:" #. type: Plain text #: ../en/multiplayer.txt:84 #, no-wrap msgid " $ git bundle create newbundle HEAD ^lastbundle\n" msgstr " $ git bundle create neuesbundle HEAD ^letztesbundle\n" #. type: Plain text #: ../en/multiplayer.txt:86 msgid "=== Patches: The Global Currency ===" msgstr "=== Patches: Das globale Zahlungsmittel ===" #. type: Plain text #: ../en/multiplayer.txt:92 msgid "" "Patches are text representations of your changes that can be easily " "understood by computers and humans alike. This gives them universal appeal. " "You can email a patch to developers no matter what version control system " "they're using. As long as your audience can read their email, they can see " "your edits. Similarly, on your side, all you require is an email account: " "there's no need to setup an online Git repository." msgstr "" "'Patches' sind die Klartextdarstellung Deiner Änderungen, die von Computern " "und Menschen gleichermaßen einfach verstanden werden. Dies verleiht ihnen " "eine universelle Anziehungskraft. Du kannst einen 'Patch' Entwicklern " "schicken, ganz egal, was für ein Versionsverwaltungssystem sie benutzen. " "Solange Deine Mitstreiter ihre eMails lesen können, können sie auch Deine " "Änderungen sehen. Auch auf Deiner Seite ist alles was Du brauchst ein eMail-" "Konto: es gibt keine Notwendigkeit ein Online Git 'Repository' aufzusetzen." #. type: Plain text #: ../en/multiplayer.txt:94 msgid "Recall from the first chapter:" msgstr "Erinnere Dich an das erste Kapitel:" #. type: Plain text #: ../en/multiplayer.txt:96 #, no-wrap msgid " $ git diff 1b6d > my.patch\n" msgstr " $ git diff 1b6d > mein.patch\n" #. type: Plain text #: ../en/multiplayer.txt:99 msgid "" "outputs a patch which can be pasted into an email for discussion. In a Git " "repository, type:" msgstr "" "gibt einen 'Patch' aus, der zur Diskussion einfach in eine eMail eingefügt " "werden kann. In einem Git 'Repository' gib ein:" #. type: Plain text #: ../en/multiplayer.txt:101 #, no-wrap msgid " $ git apply < my.patch\n" msgstr " $ git apply < mein.patch\n" #. type: Plain text #: ../en/multiplayer.txt:103 msgid "to apply the patch." msgstr "um den 'Patch' anzuwenden." #. type: Plain text #: ../en/multiplayer.txt:106 msgid "" "In more formal settings, when author names and perhaps signatures should be " "recorded, generate the corresponding patches past a certain point by typing:" msgstr "" "In einer offizielleren Umgebung, wenn Autorennamen und eventuell Signaturen " "aufgezeichnet werden sollen, erstelle die entsprechenden 'Patches' nach " "einem bestimmten Punkt durch Eingabe von:" #. type: Plain text #: ../en/multiplayer.txt:108 #, no-wrap msgid " $ git format-patch 1b6d\n" msgstr " $ git format-patch 1b6d\n" #. type: Plain text #: ../en/multiplayer.txt:110 msgid "" "The resulting files can be given to *git-send-email*, or sent by hand. You " "can also specify a range of commits:" msgstr "" "Die resultierenden Dateien können an *git-send-email* übergeben werden oder " "von Hand verschickt werden. Du kannst auch eine Gruppe von 'Commits' angeben:" #. type: Plain text #: ../en/multiplayer.txt:112 #, no-wrap msgid " $ git format-patch 1b6d..HEAD^^\n" msgstr " $ git format-patch 1b6d..HEAD^^\n" #. type: Plain text #: ../en/multiplayer.txt:114 msgid "On the receiving end, save an email to a file, then type:" msgstr "" "Auf der Empfängerseite speichere die eMail in eine Datei, dann gib ein:" #. type: Plain text #: ../en/multiplayer.txt:116 #, no-wrap msgid " $ git am < email.txt\n" msgstr " $ git am < email.txt\n" #. type: Plain text #: ../en/multiplayer.txt:118 msgid "" "This applies the incoming patch and also creates a commit, including " "information such as the author." msgstr "" "Das wendet den eingegangenen 'Patch' an und erzeugt einen 'Commit', " "inklusive der Informationen wie z.B. den Autor." #. type: Plain text #: ../en/multiplayer.txt:120 msgid "" "With a browser email client, you may need to click a button to see the email " "in its raw original form before saving the patch to a file." msgstr "" "Mit einer Webmail Anwendung musst Du eventuell ein Button anklicken um die " "eMail in ihrem rohen Originalformat anzuzeigen, bevor Du den 'Patch' in eine " "Datei sicherst." #. type: Plain text #: ../en/multiplayer.txt:124 msgid "" "There are slight differences for mbox-based email clients, but if you use " "one of these, you're probably the sort of person who can figure them out " "easily without reading tutorials!" msgstr "" "Es gibt geringfügige Unterschiede bei mbox-basierten eMail Anwendungen, aber " "wenn Du eine davon benutzt, gehörst Du vermutlich zu der Gruppe Personen, " "die damit einfach umgehen können ohne Anleitungen zu lesen.!" #. type: Plain text #: ../en/multiplayer.txt:126 msgid "=== Sorry, We've Moved ===" msgstr "=== Entschuldigung, wir sind umgezogen. ===" #. type: Plain text #: ../en/multiplayer.txt:130 msgid "" "After cloning a repository, running *git push* or *git pull* will " "automatically push to or pull from the original URL. How does Git do this? " "The secret lies in config options created with the clone. Let's take a peek:" msgstr "" "Nach dem 'Clonen' eines 'Repositories', wird *git push* oder *git pull* " "automatisch auf die original URL zugreifen. Wie macht Git das? Das Geheimnis " "liegt in der Konfiguration, die beim 'Clonen' erzeugt wurde. Lasst uns einen " "Blick riskieren:" #. type: Plain text #: ../en/multiplayer.txt:132 #, no-wrap msgid " $ git config --list\n" msgstr " $ git config --list\n" #. type: Plain text #: ../en/multiplayer.txt:136 msgid "" "The +remote.origin.url+ option controls the source URL; ``origin'' is a " "nickname given to the source repository. As with the ``master'' branch " "convention, we may change or delete this nickname but there is usually no " "reason for doing so." msgstr "" "Die +remote.origin.url+ Option kontrolliert die Quell-URL; ``origin'' ist " "der Spitzname, der dem Quell-'Repository' gegeben wurde. Wie mit der " "``master'' 'Branch' Konvention können wir diesen Spitznamen ändern oder " "löschen, aber es gibt für gewöhnlich keinen Grund dies zu tun." #. type: Plain text #: ../en/multiplayer.txt:138 msgid "If the original repository moves, we can update the URL via:" msgstr "" "Wenn das original 'Repository' verschoben wird, können wir die URL " "aktualisieren mit:" #. type: Plain text #: ../en/multiplayer.txt:140 #, no-wrap msgid " $ git config remote.origin.url git://new.url/proj.git\n" msgstr " $ git config remote.origin.url git://neue.url/proj.git\n" #. type: Plain text #: ../en/multiplayer.txt:146 msgid "" "The +branch.master.merge+ option specifies the default remote branch in a " "*git pull*. During the initial clone, it is set to the current branch of the " "source repository, so even if the HEAD of the source repository subsequently " "moves to a different branch, a later pull will faithfully follow the " "original branch." msgstr "" "Die +branch.master.merge+ Option definiert den Standard-Remote-'Branch' bei " "einem *git pull*. Während dem ursprünglichen 'clonen', wird sie auf den " "aktuellen 'Branch' des Quell-'Repository' gesetzt, so dass selbst dann, wenn " "der 'HEAD' des Quell-'Repository' inzwischen auf einen anderen 'Branch' " "gewechselt hat, ein späterer 'pull' wird treu dem original 'Branch' folgen." #. type: Plain text #: ../en/multiplayer.txt:150 msgid "" "This option only applies to the repository we first cloned from, which is " "recorded in the option +branch.master.remote+. If we pull in from other " "repositories we must explicitly state which branch we want:" msgstr "" "Diese Option gilt nur für das 'Repository', von dem als erstes 'gecloned' " "wurde, was in der Option +branch.master.remote+ hinterlegt ist. Bei einem " "'pull' aus anderen 'Repositories' müssen wir explizit angeben, welchen " "'Branch' wir wollen:" #. type: Plain text #: ../en/multiplayer.txt:152 #, no-wrap msgid " $ git pull git://example.com/other.git master\n" msgstr " $ git pull git://beispiel.com/anderes.git master\n" #. type: Plain text #: ../en/multiplayer.txt:155 msgid "" "The above explains why some of our earlier push and pull examples had no " "arguments." msgstr "" "Das obige erklärt, warum einige von unseren früheren 'push' und 'pull' " "Beispielen keine Argumente hatten." #. type: Plain text #: ../en/multiplayer.txt:157 msgid "=== Remote Branches ===" msgstr "=== Entfernte 'Branches' ===" #. type: Plain text #: ../en/multiplayer.txt:162 msgid "" "When you clone a repository, you also clone all its branches. You may not " "have noticed this because Git hides them away: you must ask for them " "specifically. This prevents branches in the remote repository from " "interfering with your branches, and also makes Git easier for beginners." msgstr "" "Wenn Du ein 'Repository' 'clonst', 'clonst' Du auch alle seine 'Branches'. " "Das hast Du vielleicht noch nicht bemerkt, denn Git versteckt diese: Du " "musst speziell danach fragen. Das verhindert, dass 'Branches' vom entfernten " "'Repository' Deine lokalen 'Branches' stören und es macht Git einfacher für " "Anfänger." #. type: Plain text #: ../en/multiplayer.txt:164 msgid "List the remote branches with:" msgstr "Zeige die entfernten 'Branches' an mit:" #. type: Plain text #: ../en/multiplayer.txt:166 #, no-wrap msgid " $ git branch -r\n" msgstr " $ git branch -r\n" #. type: Plain text #: ../en/multiplayer.txt:168 msgid "You should see something like:" msgstr "Du solltes etwas sehen wie:" #. type: Plain text #: ../en/multiplayer.txt:172 #, no-wrap msgid "" " origin/HEAD\n" " origin/master\n" " origin/experimental\n" msgstr "" " origin/HEAD\n" " origin/master\n" " origin/experimentell\n" #. type: Plain text #: ../en/multiplayer.txt:177 msgid "" "These represent branches and the HEAD of the remote repository, and can be " "used in regular Git commands. For example, suppose you have made many " "commits, and wish to compare against the last fetched version. You could " "search through the logs for the appropriate SHA1 hash, but it's much easier " "to type:" msgstr "" "Diese Liste zeigt die 'Branches' und den HEAD des entfernten 'Repository', " "welche auch in regulären Git Anweisungen verwendet werden können. Zum " "Beispiel, angenommen Du hast viele 'Commits' gemacht und möchtest einen " "Vergleich zur letzten abgeholten Version machen. Du kannst die Logs nach dem " "entsprechenden SHA1 Hashwert durchsuchen, aber es ist viel einfacher " "folgendes einzugeben:" #. type: Plain text #: ../en/multiplayer.txt:179 #, no-wrap msgid " $ git diff origin/HEAD\n" msgstr " $ git diff origin/HEAD\n" #. type: Plain text #: ../en/multiplayer.txt:181 msgid "Or you can see what the ``experimental'' branch has been up to:" msgstr "" "Oder Du kannst schauen, was auf dem 'Branch' ``experimentell'' los war:" #. type: Plain text #: ../en/multiplayer.txt:183 #, no-wrap msgid " $ git log origin/experimental\n" msgstr " $ git log origin/experimentell\n" #. type: Plain text #: ../en/multiplayer.txt:185 msgid "=== Multiple Remotes ===" msgstr "=== Mehrere 'Remotes' ===" #. type: Plain text #: ../en/multiplayer.txt:188 msgid "" "Suppose two other developers are working on our project, and we want to keep " "tabs on both. We can follow more than one repository at a time with:" msgstr "" "Angenommen, zwei andere Entwickler arbeiten an Deinem Projekt und wir wollen " "beide im Auge behalten. Wir können mehr als ein 'Repository' gleichzeitig " "beobachten mit:" #. type: Plain text #: ../en/multiplayer.txt:191 #, no-wrap msgid "" " $ git remote add other git://example.com/some_repo.git\n" " $ git pull other some_branch\n" msgstr "" " $ git remote add other git://example.com/some_repo.git\n" " $ git pull other some_branch\n" #. type: Plain text #: ../en/multiplayer.txt:194 msgid "" "Now we have merged in a branch from the second repository, and we have easy " "access to all branches of all repositories:" msgstr "" "Nun haben wir einen 'Branch' vom zweiten 'Repository' eingebunden und wir " "haben einfachen Zugriff auf alle 'Branches' von allen 'Repositories':" #. type: Plain text #: ../en/multiplayer.txt:196 #, no-wrap msgid " $ git diff origin/experimental^ other/some_branch~5\n" msgstr " $ git diff origin/experimentell^ other/some_branch~5\n" #. type: Plain text #: ../en/multiplayer.txt:200 msgid "" "But what if we just want to compare their changes without affecting our own " "work? In other words, we want to examine their branches without having their " "changes invade our working directory. Then rather than pull, run:" msgstr "" "Aber was, wenn wir nur deren Änderungen vergleichen wollen, ohne unsere " "eigene Arbeit zu beeinflussen? Mit anderen Worten, wir wollen ihre " "'Branches' untersuchen ohne dass deren Änderungen in unser " "Arbeitsverzeichnis einfließen. Anstatt 'pull' benutzt Du dann:" #. type: Plain text #: ../en/multiplayer.txt:203 #, no-wrap msgid "" " $ git fetch # Fetch from origin, the default.\n" " $ git fetch other # Fetch from the second programmer.\n" msgstr "" " $ git fetch # Fetch vom origin, der Standard.\n" " $ git fetch other # Fetch vom zweiten Programmierer.\n" #. type: Plain text #: ../en/multiplayer.txt:207 msgid "" "This just fetches histories. Although the working directory remains " "untouched, we can refer to any branch of any repository in a Git command " "because we now possess a local copy." msgstr "" "Dies holt lediglich die Chroniken. Obwohl das Arbeitsverzeichnis unverändert " "bleibt, können wir nun jeden 'Branch' aus jedem 'Repository' in einer Git " "Anweisung referenzieren, da wir eine lokale Kopie besitzen." #. type: Plain text #: ../en/multiplayer.txt:211 msgid "" "Recall that behind the scenes, a pull is simply a *fetch* then *merge*. " "Usually we *pull* because we want to merge the latest commit after a fetch; " "this situation is a notable exception." msgstr "" "Erinnere Dich, dass ein 'Pull' hinter den Kulissen einfach ein *fetch* " "gefolgt von einem *merge* ist. Normalerweise machen wir einen *pull* weil " "wir die letzten 'Commits' abrufen und einbinden wollen. Die beschriebene " "Situation ist eine erwähnenswerte Ausnahme." #. type: Plain text #: ../en/multiplayer.txt:214 msgid "" "See *git help remote* for how to remove remote repositories, ignore certain " "branches, and more." msgstr "" "Siehe *git help remote* um zu sehen wie man Remote-'Repositories' entfernt, " "bestimmte 'Branches' ignoriert und mehr." #. type: Plain text #: ../en/multiplayer.txt:216 msgid "=== My Preferences ===" msgstr "=== Meine Einstellungen ===" #. type: Plain text #: ../en/multiplayer.txt:220 msgid "" "For my projects, I like contributors to prepare repositories from which I " "can pull. Some Git hosting services let you host your own fork of a project " "with the click of a button." msgstr "" "Für meine Projekte bevorzuge ich es, wenn Unterstützer 'Repositories' " "vorbereiten, von denen ich 'pullen' kann. Verschiedene Git Hosting Anbieter " "lassen Dich mit einem Klick deine eigene 'Fork' eines Projekts hosten." #. type: Plain text #: ../en/multiplayer.txt:224 msgid "" "After I fetch a tree, I run Git commands to navigate and examine the " "changes, which ideally are well-organized and well-described. I merge my own " "changes, and perhaps make further edits. Once satisfied, I push to the main " "repository." msgstr "" "Nachdem ich einen Zweig abgerufen habe, benutze ich Git Anweisungen um durch " "die Änderungen zu navigieren und zu untersuchen, die idealerweise gut " "organisiert und dokumentiert sind. Ich 'merge' meine eigenen Änderungen und " "führe eventuell weitere Änderungen durch. Wenn ich zufrieden bin, 'pushe' " "ich in das zentrale 'Repository'." #. type: Plain text #: ../en/multiplayer.txt:229 msgid "" "Though I infrequently receive contributions, I believe this approach scales " "well. See http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm." "html[this blog post by Linus Torvalds]." msgstr "" "Obwohl ich nur unregelmäßig Beiträge erhalte, glaube ich, dass diese Methode " "sich auszahlt. Siehe http://torvalds-family.blogspot.com/2009/06/happiness-" "is-warm-scm.html[diesen Blog Beitrag von Linus Torvalds (englisch)]." #. type: Plain text #: ../en/multiplayer.txt:233 msgid "" "Staying in the Git world is slightly more convenient than patch files, as it " "saves me from converting them to Git commits. Furthermore, Git handles " "details such as recording the author's name and email address, as well as " "the time and date, and asks the author to describe their own change." msgstr "" "In der Git Welt zu bleiben ist etwas bequemer als 'Patch'-Dateien, denn es " "erspart mir sie in Git 'Commits' zu konvertieren. Außerdem kümmert sich Git " "um die Details wie Autorname und eMail-Adresse, genauso wie um Datum und " "Uhrzeit und es fordert den Autor zum Beschreiben seiner eigenen Änderungen " "auf." gitmagic-20160304/de/pot/history.po0000644000175000017500000006626112666307504016246 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010, 2011. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: bennlynn@gmail.com\n" "POT-Creation-Date: 2010-10-30 08:21+0300\n" "PO-Revision-Date: 2011-07-03 15:23+0200\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/history.txt:2 msgid "== Lessons of History ==" msgstr "== Geschichtsstunde ==" #. type: Plain text #: ../en/history.txt:8 msgid "" "A consequence of Git's distributed nature is that history can be edited " "easily. But if you tamper with the past, take care: only rewrite that part " "of history which you alone possess. Just as nations forever argue over who " "committed what atrocity, if someone else has a clone whose version of " "history differs to yours, you will have trouble reconciling when your trees " "interact." msgstr "" "Eine Folge von Git's verteilter Natur ist, dass die Chronik einfach " "verändert werden kann. Aber, wenn du an der Vergangenheit manipulierst, sei " "vorsichtig: verändere nur den Teil der Chronik, den du ganz alleine hast. So " "wie Nationen ewig diskutieren, wer welche Greueltaten vollbracht hat, wirst " "du beim Abgleichen in Schwierigkeiten geraten, falls jemand einen 'Clone' " "mit abweichender Chronik hat und die Zweige sich austauschen sollen." #. type: Plain text #: ../en/history.txt:14 msgid "" "Some developers strongly feel history should be immutable, warts and all. " "Others feel trees should be made presentable before they are unleashed in " "public. Git accommodates both viewpoints. Like cloning, branching, and " "merging, rewriting history is simply another power Git gives you. It is up " "to you to use it wisely." msgstr "" "Einige Entwickler setzen sich nachhaltig für die Unantastbarkeit der Chronik " "ein, mit allen Fehlern, Nachteilen und Mängeln. Andere denken, daß Zweige " "vorzeigbar gemacht werden sollten, bevor sie auf die Öffentlichkeit " "losgelassen werden. Git versteht beide Gesichtspunkte. Wie 'Clonen', " "'Branchen' und 'Mergen' ist das Umschreiben der Chronik lediglich eine " "weitere Stärke, die Git dir bietet. Es liegt an dir diese weise zu nutzen." #. type: Plain text #: ../en/history.txt:16 msgid "=== I Stand Corrected ===" msgstr "=== Ich nehme alles zurück ===" #. type: Plain text #: ../en/history.txt:18 msgid "" "Did you just commit, but wish you had typed a different message? Then run:" msgstr "" "Hast du gerade 'commitet', aber du hättest gerne eine andere Beschreibung " "eingegeben? Dann gib ein:" #. type: Plain text #: ../en/history.txt:20 ../en/history.txt:54 #, no-wrap msgid " $ git commit --amend\n" msgstr " $ git commit --amend\n" #. type: Plain text #: ../en/history.txt:23 msgid "" "to change the last message. Realized you forgot to add a file? Run *git add* " "to add it, and then run the above command." msgstr "" "um die letzte Beschreibung zu ändern. Du merkst, dass du vergessen hast eine " "Datei hinzuzufügen? Führe *git add* aus um sie hinzuzufügen und dann die " "vorhergehende Anweisung." #. type: Plain text #: ../en/history.txt:25 msgid "" "Want to include a few more edits in that last commit? Then make those edits " "and run:" msgstr "" "Du willst noch ein paar Änderungen zu deinem letzten 'Commit' hinzufügen? " "Dann mache diese Änderungen und gib ein:" #. type: Plain text #: ../en/history.txt:27 #, no-wrap msgid " $ git commit --amend -a\n" msgstr " $ git commit --amend -a\n" #. type: Plain text #: ../en/history.txt:29 msgid "=== ... And Then Some ===" msgstr "=== ... und noch viel mehr ===" #. type: Plain text #: ../en/history.txt:31 msgid "" "Let's suppose the previous problem is ten times worse. After a lengthy " "session you've made a bunch of commits. But you're not quite happy with the " "way they're organized, and some of those commit messages could use " "rewording. Then type:" msgstr "" "Nehmen wir jetzt an, das vorherige Problem ist zehnmal schlimmer. Nach einer " "längeren Sitzung hast du einen Haufen 'Commits' gemacht. Aber du bist mit " "der Art der Organisation nicht glücklich und einige 'Commits' könnten etwas " "umformuliert werden. Dann gib ein:" #. type: Plain text #: ../en/history.txt:33 #, no-wrap msgid " $ git rebase -i HEAD~10\n" msgstr " $ git rebase -i HEAD~10\n" #. type: Plain text #: ../en/history.txt:35 msgid "" "and the last 10 commits will appear in your favourite $EDITOR. A sample " "excerpt:" msgstr "" "und die letzten zehn 'Commits' erscheinen in deinem bevorzugten $EDITOR. " "Auszug aus einem Beispiel:" #. type: Plain text #: ../en/history.txt:39 #, no-wrap msgid "" " pick 5c6eb73 Added repo.or.cz link\n" " pick a311a64 Reordered analogies in \"Work How You Want\"\n" " pick 100834f Added push target to Makefile\n" msgstr "" " pick 5c6eb73 Link repo.or.cz hinzugefügt\n" " pick a311a64 Analogien in \"Arbeite wie du willst\" umorganisiert\n" " pick 100834f Push-Ziel zum Makefile hinzugefügt\n" #. type: Plain text #: ../en/history.txt:41 msgid "Then:" msgstr "Dann:" #. type: Bullet: '- ' #: ../en/history.txt:49 msgid "Remove commits by deleting lines." msgstr "Entferne 'Commits' durch das Löschen von Zeilen." #. type: Bullet: '- ' #: ../en/history.txt:49 msgid "Reorder commits by reordering lines." msgstr "Organisiere 'Commits' durch verschieben von Zeilen." #. type: Plain text #: ../en/history.txt:49 #, no-wrap msgid "" "- Replace `pick` with:\n" " * `edit` to mark a commit for amending.\n" " * `reword` to change the log message.\n" " * `squash` to merge a commit with the previous one.\n" " * `fixup` to merge a commit with the previous one and discard the log message.\n" msgstr "" "- Ersetze `pick` mit:\n" " * `edit` um einen 'Commit' für 'amends' zu markieren.\n" " * `reword` um die Log-Beschreibung zu ändern.\n" " * `squash` um einen 'Commit' mit dem vorhergehenden zu vereinen ('merge').\n" " * `fixup` um einen 'Commit' mit dem vorhergehenden zu vereinen ('merge') und die Log-Beschreibung zu verwerfen.\n" #. type: Plain text #: ../en/history.txt:52 msgid "Save and quit. If you marked a commit for editing, then run:" msgstr "" "Speichere und Beende. Wenn du einen 'Commit' mit 'edit' markiert hast, gib " "ein:" #. type: Plain text #: ../en/history.txt:56 msgid "Otherwise, run:" msgstr "Ansonsten:" #. type: Plain text #: ../en/history.txt:58 #, no-wrap msgid " $ git rebase --continue\n" msgstr " $ git rebase --continue\n" #. type: Plain text #: ../en/history.txt:60 msgid "So commit early and commit often: you can tidy up later with rebase." msgstr "Also 'commite' früh und oft: du kannst später mit 'rebase' aufräumen." #. type: Plain text #: ../en/history.txt:62 msgid "=== Local Changes Last ===" msgstr "=== Lokale Änderungen zum Schluß ===" #. type: Plain text #: ../en/history.txt:65 msgid "" "You're working on an active project. You make some local commits over time, " "and then you sync with the official tree with a merge. This cycle repeats " "itself a few times before you're ready to push to the central tree." msgstr "" "Du arbeitest an einem aktiven Projekt. Über die Zeit haben sich einige " "lokale 'Commits' angesammelt und dann synchronisierst du mit einem 'Merge' " "mit dem offiziellen Zweig. Dieser Zyklus wiederholt sich ein paar Mal bevor " "du zum 'Pushen' in den zentralen Zweig bereit bist." #. type: Plain text #: ../en/history.txt:67 msgid "" "But now the history in your local Git clone is a messy jumble of your " "changes and the official changes. You'd prefer to see all your changes in " "one contiguous section, and after all the official changes." msgstr "" "Aber nun ist die Chronik in deinem lokalen Git-'Clone' ein chaotisches " "Durcheinander deiner Änderungen und den Änderungen vom offiziellen Zweig. Du " "willst alle Deine Änderungen lieber in einem fortlaufenden Abschnitt und " "hinter den offiziellen Änderungen sehen." #. type: Plain text #: ../en/history.txt:70 msgid "" "This is a job for *git rebase* as described above. In many cases you can use " "the *--onto* flag and avoid interaction." msgstr "" "Das ist eine Aufgabe für *git rebase*, wie oben beschrieben. In vielen " "Fällen kannst du den *--onto* Schalter benutzen um Interaktion zu vermeiden." #. type: Plain text #: ../en/history.txt:72 msgid "" "Also see *git help rebase* for detailed examples of this amazing command. " "You can split commits. You can even rearrange branches of a tree." msgstr "" "Siehe auch *git help rebase* für ausführliche Beispiele dieser erstaunlichen " "Anweisung. Du kannst auch 'Commits' aufteilen. Du kannst sogar 'Branches' in " "einem 'Repository' umorganisieren." #. type: Plain text #: ../en/history.txt:74 msgid "=== Rewriting History ===" msgstr "=== Chronik umschreiben ===" #. type: Plain text #: ../en/history.txt:82 msgid "" "Occasionally, you need the source control equivalent of airbrushing people " "out of official photos, erasing them from history in a Stalinesque fashion. " "For example, suppose we intend to release a project, but it involves a file " "that should be kept private for some reason. Perhaps I left my credit card " "number in a text file and accidentally added it to the project. Deleting the " "file is insufficient, for the file can be accessed from older commits. We " "must remove the file from all commits:" msgstr "" "Gelegentlich brauchst du Versionsverwaltung vergleichbar dem Wegretuschieren " "von Personen aus einem offiziellen Foto, um diese in stalinistischer Art aus " "der Geschichte zu löschen. Stell dir zum Beispiel vor, du willst ein Projekt " "veröffentlichen, aber es enthält eine Datei, die aus irgendwelchen Gründen " "privat bleiben muss. Vielleicht habe ich meine Kreditkartennummer in einer " "Textdatei notiert und diese versehentlich dem Projekt hinzugefügt. Die Datei " "zu löschen ist zwecklos, da über ältere 'Commits' auf sie zugegriffen werden " "könnte. Wir müssen die Datei aus allen 'Commits' entfernen:" #. type: Plain text #: ../en/history.txt:84 #, no-wrap msgid " $ git filter-branch --tree-filter 'rm top/secret/file' HEAD\n" msgstr " $ git filter-branch --tree-filter 'rm sehr/geheime/Datei' HEAD\n" #. type: Plain text #: ../en/history.txt:88 msgid "" "See *git help filter-branch*, which discusses this example and gives a " "faster method. In general, *filter-branch* lets you alter large sections of " "history with a single command." msgstr "" "Siehe *git help filter-branch*, wo dieses Beispiel erklärt und eine " "schnellere Methode vorstellt wird. Allgemein, *filter-branch* lässt dich " "große Bereiche der Chronik mit einer einzigen Anweisung verändern." #. type: Plain text #: ../en/history.txt:90 msgid "" "Afterwards, the +.git/refs/original+ directory describes the state of " "affairs before the operation. Check the filter-branch command did what you " "wanted, then delete this directory if you wish to run more filter-branch " "commands." msgstr "" "Danach beschreibt der Ordner +.git/refs/original+ den Zustand der Lage vor " "der Operation. Prüfe, ob die 'filter-branch' Anweisung getan hat was du " "wolltest, dann lösche dieses Verzeichnis bevor du weitere 'filter-branch' " "Operationen durchführst." #. type: Plain text #: ../en/history.txt:92 msgid "" "Lastly, replace clones of your project with your revised version if you want " "to interact with them later." msgstr "" "Zuletzt, ersetze alle 'Clones' deines Projekts mit deiner überarbeiteten " "Version, falls du später mit ihnen interagieren möchtest." #. type: Plain text #: ../en/history.txt:94 msgid "=== Making History ===" msgstr "=== Geschichte machen ===" #. type: Plain text #: ../en/history.txt:97 msgid "" "[[makinghistory]] Want to migrate a project to Git? If it's managed with one " "of the more well-known systems, then chances are someone has already written " "a script to export the whole history to Git." msgstr "" "[[makinghistory]] Du möchtest ein Projekt zu Git umziehen? Wenn es mit einem " "der bekannteren Systeme verwaltet wird, besteht die Möglichkeit, dass schon " "jemand ein Skript geschrieben hat, das die gesamte Chronik für Git " "exportiert." #. type: Plain text #: ../en/history.txt:102 msgid "" "Otherwise, look up *git fast-import*, which reads text input in a specific " "format to create Git history from scratch. Typically a script using this " "command is hastily cobbled together and run once, migrating the project in a " "single shot." msgstr "" "Anderenfalls, sieh dir *git fast-import* an, das Text in einem speziellen " "Format einliest um eine Git Chronik von Anfang an zu erstellen. " "Normalerweise wird ein Skript, das diese Anweisung benutzt, hastig " "zusammengeschustert und einmalig ausgeführt um das Projekt in einem einzigen " "Lauf zu migrieren." #. type: Plain text #: ../en/history.txt:104 #, no-wrap msgid "" "As an example, paste the following listing into temporary file, such as `/tmp/history`:\n" "----------------------------------\n" msgstr "" "Erstelle zum Beispiel aus folgendem Listing eine temporäre Datei, z.B. `/tmp/history`:\n" "----------------------------------\n" #. type: Plain text #: ../en/history.txt:110 msgid "" "commit refs/heads/master committer Alice Thu, 01 Jan " "1970 00:00:00 +0000 data < Thu, 01 Jan " "1970 00:00:00 +0000 data <" msgstr "M 100644 inline hello.c data <" #. type: Plain text #: ../en/history.txt:120 #, no-wrap msgid "" "int main() {\n" " printf(\"Hello, world!\\n\");\n" " return 0;\n" "}\n" "EOT\n" msgstr "" "int main() {\n" " printf(\"Hallo, Welt!\\n\");\n" " return 0;\n" "}\n" "EOT\n" #. type: Plain text #: ../en/history.txt:127 msgid "" "commit refs/heads/master committer Bob Tue, 14 Mar 2000 " "01:59:26 -0800 data < Tue, 14 Mar 2000 " "01:59:26 -0800 data <" msgstr "M 100644 inline hello.c data <" #. type: Plain text #: ../en/history.txt:137 #, no-wrap msgid "" "int main() {\n" " write(1, \"Hello, world!\\n\", 14);\n" " return 0;\n" "}\n" "EOT\n" msgstr "" "int main() {\n" " write(1, \"Hallo, Welt!\\n\", 14);\n" " return 0;\n" "}\n" "EOT\n" #. type: Plain text #: ../en/history.txt:138 #, no-wrap msgid "----------------------------------\n" msgstr "----------------------------------\n" #. type: Plain text #: ../en/history.txt:141 msgid "Then create a Git repository from this temporary file by typing:" msgstr "" "Dann, erstelle ein Git 'Repository' aus dieser temporären Datei, durch " "Eingabe von:" #. type: Plain text #: ../en/history.txt:144 #, no-wrap msgid "" " $ mkdir project; cd project; git init\n" " $ git fast-import --date-format=rfc2822 < /tmp/history\n" msgstr "" " $ mkdir project; cd project; git init\n" " $ git fast-import --date-format=rfc2822 < /tmp/history\n" #. type: Plain text #: ../en/history.txt:146 msgid "You can checkout the latest version of the project with:" msgstr "" "Die aktuellste Version des Projekts kannst du abrufen ('checkout') mit:" #. type: Plain text #: ../en/history.txt:148 #, no-wrap msgid " $ git checkout master .\n" msgstr " $ git checkout master .\n" #. type: Plain text #: ../en/history.txt:153 msgid "" "The *git fast-export* command converts any repository to the *git fast-" "import* format, whose output you can study for writing exporters, and also " "to transport repositories in a human-readable format. Indeed, these commands " "can send repositories of text files over text-only channels." msgstr "" "Die Anweisung *git fast-export* konvertiert jedes 'Repository' in das *git " "fast-import* Format, diese Ausgabe kannst du studieren um Exporteure zu " "schreiben und außerdem um 'Repositories' in Klartext zu übertragen. " "untersuchen wes you can study for writing exporters, and also to transport " "repositories in a human-readable format. Wirklich, diese Anweisung kann " "Klartext-'Repositories' über reine Textkanäle übertragen." #. type: Plain text #: ../en/history.txt:155 msgid "=== Where Did It All Go Wrong? ===" msgstr "=== Wo ging alles schief? ===" #. type: Plain text #: ../en/history.txt:157 msgid "" "You've just discovered a broken feature in your program which you know for " "sure was working a few months ago. Argh! Where did this bug come from? If " "only you had been testing the feature as you developed." msgstr "" "Du hast gerade ein Funktion in deiner Anwendung entdeckt, die nicht mehr " "funktioniert und du weißt sicher, dass sie vor ein paar Monaten noch ging. " "Argh! Wo kommt dieser Fehler her? Hättest du nur die Funktion während der " "Entwicklung getestet." #. type: Plain text #: ../en/history.txt:160 msgid "" "It's too late for that now. However, provided you've been committing often, " "Git can pinpoint the problem:" msgstr "" "Dafür ist es nun zu spät. Wie auch immer, vorausgesetzt du hast oft " "'comittet', kann Git dir sagen, wo das Problem liegt:" #. type: Plain text #: ../en/history.txt:164 #, no-wrap msgid "" " $ git bisect start\n" " $ git bisect bad HEAD\n" " $ git bisect good 1b6d\n" msgstr "" " $ git bisect start\n" " $ git bisect bad HEAD\n" " $ git bisect good 1b6d\n" #. type: Plain text #: ../en/history.txt:166 msgid "" "Git checks out a state halfway in between. Test the feature, and if it's " "still broken:" msgstr "" "Git ruft eine Stand ab, der genau dazwischen liegt. Teste die Funktion und " "wenn sich immer noch nicht funktioniert:" #. type: Plain text #: ../en/history.txt:168 #, no-wrap msgid " $ git bisect bad\n" msgstr " $ git bisect bad\n" #. type: Plain text #: ../en/history.txt:174 msgid "" "If not, replace \"bad\" with \"good\". Git again transports you to a state " "halfway between the known good and bad versions, narrowing down the " "possibilities. After a few iterations, this binary search will lead you to " "the commit that caused the trouble. Once you've finished your investigation, " "return to your original state by typing:" msgstr "" "Wenn nicht, ersetzte \"bad\" mit \"good\". Git versetzt dich wieder auf " "einen Stand genau zwischen den bekannten Versionen \"good\" und \"bad\" und " "reduziert so die Möglichkeiten. Nach ein paar Durchläufen wird dich diese " "binäre Suche zu dem 'Commit' führen, der die Probleme verursacht. Wenn du " "deine Ermittlungen abgeschlossen hast, kehre zum Originalstand zurück mit:" #. type: Plain text #: ../en/history.txt:176 #, no-wrap msgid " $ git bisect reset\n" msgstr " $ git bisect reset\n" #. type: Plain text #: ../en/history.txt:178 msgid "" "Instead of testing every change by hand, automate the search by running:" msgstr "" "Anstatt jede Änderung per Hand zu untersuchen, automatisiere die Suche durch " "Ausführen von:" #. type: Plain text #: ../en/history.txt:180 #, no-wrap msgid " $ git bisect run my_script\n" msgstr " $ git bisect run mein_skript\n" #. type: Plain text #: ../en/history.txt:185 msgid "" "Git uses the return value of the given command, typically a one-off script, " "to decide whether a change is good or bad: the command should exit with code " "0 when good, 125 when the change should be skipped, and anything else " "between 1 and 127 if it is bad. A negative return value aborts the bisect." msgstr "" "Git benutzt den Rückgabewert der übergebenen Anweisung, normalerweise ein " "Skript für einmalige Ausführung, um zu entscheiden, ob eine Änderung gut " "('good') oder schlecht ('bad') ist: Das Skript sollte 0 für 'good' " "zurückgeben, 125 wenn die Änderung übersprungen werden soll und irgendetwas " "zwischen 1 und 127 für 'bad'. Ein negativer Rückgabewert beendet die " "'bisect'-Operation sofort." #. type: Plain text #: ../en/history.txt:189 msgid "" "You can do much more: the help page explains how to visualize bisects, " "examine or replay the bisect log, and eliminate known innocent changes for a " "speedier search." msgstr "" "Du kannst noch viel mehr machen: die Hilfe erklärt, wie man 'bisect'-" "Operationen visualisiert, das 'bisect'-Log untersucht oder wiedergibt und " "sicher unschuldige Änderungen ausschließt um die Suche zu beschleunigen." #. type: Plain text #: ../en/history.txt:191 msgid "=== Who Made It All Go Wrong? ===" msgstr "=== Wer ist verantwortlich? ===" #. type: Plain text #: ../en/history.txt:193 msgid "Like many other version control systems, Git has a blame command:" msgstr "" "Wie viele andere Versionsverwaltungssysteme hat Git eine 'blame' Anweisung:" #. type: Plain text #: ../en/history.txt:195 #, no-wrap msgid " $ git blame bug.c\n" msgstr " $ git blame bug.c\n" #. type: Plain text #: ../en/history.txt:197 msgid "" "which annotates every line in the given file showing who last changed it, " "and when. Unlike many other version control systems, this operation works " "offline, reading only from local disk." msgstr "" "das jede Zeile in der angegebenen Datei kommentiert um anzuzeigen, wer sie " "zuletzt geändert hat und wann. Im Gegensatz zu vielen anderen " "Versionsverwaltungssystemen funktioniert diese Operation offline, es wird " "nur von der lokalen Festplatte gelesen." #. type: Plain text #: ../en/history.txt:199 msgid "=== Personal Experience ===" msgstr "=== Persönliche Erfahrungen ===" #. type: Plain text #: ../en/history.txt:206 msgid "" "In a centralized version control system, history modification is a difficult " "operation, and only available to administrators. Cloning, branching, and " "merging are impossible without network communication. So are basic " "operations such as browsing history, or committing a change. In some " "systems, users require network connectivity just to view their own changes " "or open a file for editing." msgstr "" "In einem zentralisierten Versionsverwaltungssystem ist das Bearbeiten der " "Chronik eine schwierige Angelegenheit und den Administratoren vorbehalten. " "'Clonen', 'Branchen' und 'Mergen' sind unmöglich ohne Netzwerkverbindung. " "Ebenso grundlegende Funktionen wie das Durchsuchen der Chronik oder das " "'comitten' einer Änderung. In manchen Systemen benötigt der Anwender schon " "eine Netzwerkverbindung nur um seine eigenen Änderungen zu sehen oder um " "eine Datei zum Bearbeiten zu öffnen." #. type: Plain text #: ../en/history.txt:213 msgid "" "Centralized systems preclude working offline, and need more expensive " "network infrastructure, especially as the number of developers grows. Most " "importantly, all operations are slower to some degree, usually to the point " "where users shun advanced commands unless absolutely necessary. In extreme " "cases this is true of even the most basic commands. When users must run slow " "commands, productivity suffers because of an interrupted work flow." msgstr "" "Zentralisierte Systeme schließen es aus offline zu arbeiten und benötigen " "teurere Netzwerkinfrastruktur, vor allem, wenn die Zahl der Entwickler " "steigt. Am wichtigsten ist, dass alle Operationen bis zu einem gewissen Grad " "langsamer sind, in der Regel bis zu dem Punkt, wo Anwender erweiterte " "Anweisungen scheuen, bis sie absolut notwendig sind. In extremen Fällen " "trifft das auch auf die grundlegenden Anweisungen zu. Wenn Anwender langsame " "Anweisungen ausführen müssen, sinkt die Produktivität, da der Arbeitsfluss " "unterbrochen wird." #. type: Plain text #: ../en/history.txt:219 msgid "" "I experienced these phenomena first-hand. Git was the first version control " "system I used. I quickly grew accustomed to it, taking many features for " "granted. I simply assumed other systems were similar: choosing a version " "control system ought to be no different from choosing a text editor or web " "browser." msgstr "" "Ich habe diese Phänomen aus erster Hand erfahren. Git war das erste " "Versionsverwaltungssystem, das ich benutzt habe. Ich bin schnell in die " "Anwendung hineingewachsen und betrachtete viele Funktionen als " "selbstverständlich. Ich habe einfach vorausgesetzt, dass andere Systeme " "ähnlich sind: die Auswahl eines Versionsverwaltungssystems sollte nicht " "anders sein als die Auswahl eines Texteditors oder Internetbrowser." #. type: Plain text #: ../en/history.txt:225 msgid "" "I was shocked when later forced to use a centralized system. A flaky " "internet connection matters little with Git, but makes development " "unbearable when it needs to be as reliable as local disk. Additionally, I " "found myself conditioned to avoid certain commands because of the latencies " "involved, which ultimately prevented me from following my desired work flow." msgstr "" "Ich war geschockt, als ich später gezwungen war ein zentralisiertes System " "zu benutzen. Eine unzuverlässige Internetverbindung stört mit Git nicht " "sehr, aber sie macht die Entwicklung unerträglich, wenn sie so zuverlässig " "wie ein lokale Festplatte sein sollte. Zusätzlich habe ich mich dabei " "ertappt, bestimmte Anweisungen zu vermeiden, um die damit verbundenen " "Wartezeiten zu vermeiden und das hat mich letztendlich davon abgehalten " "meinem gewohnten Arbeitsablauf zu folgen." #. type: Plain text #: ../en/history.txt:232 msgid "" "When I had to run a slow command, the interruption to my train of thought " "dealt a disproportionate amount of damage. While waiting for server " "communication to complete, I'd do something else to pass the time, such as " "check email or write documentation. By the time I returned to the original " "task, the command had finished long ago, and I would waste more time trying " "to remember what I was doing. Humans are bad at context switching." msgstr "" "Wenn ich eine langsame Anweisung auszuführen hatte, wurde durch die " "Unterbrechung meiner Gedankengänge dem Arbeitsfluss ein unverhältnismäßiger " "Schaden zugefügt. Während dem Warten auf das Ende der Serverkommunikation " "tat ich etwas anderes um die Wartezeit zu überbrücken, zum Beispiel E-Mails " "lesen oder Dokumentation schreiben. Wenn ich zur ursprünglichen Arbeit " "zurückkehrte, war die Operation längst beendet und ich vergeudete noch mehr " "Zeit beim Versuch mich zu erinnern was ich getan habe. Menschen sind nicht " "gut im Kontextwechsel." #. type: Plain text #: ../en/history.txt:237 msgid "" "There was also an interesting tragedy-of-the-commons effect: anticipating " "network congestion, individuals would consume more bandwidth than necessary " "on various operations in an attempt to reduce future delays. The combined " "efforts intensified congestion, encouraging individuals to consume even more " "bandwidth next time to avoid even longer delays." msgstr "" "Da war auch ein interessanter http://de.wikipedia.org/wiki/" "Tragik_der_Allmende[Tragik-der-Allmende] Effekt: Netzwerküberlastungen " "erahnend, verbrauchten einzelne Individuen für diverse Operationen mehr " "Netzwerkbandbreite als erforderlich, um zukünftige Engpässe zu vermeiden. " "Die Summe der Bemühungen verschlimmerte die Überlastungen, was einzelne " "wiederum ermutigte noch mehr Bandbreite zu verbrauchen um noch längere " "Wartezeiten zu verhindern." gitmagic-20160304/de/pot/basic.po0000644000175000017500000005412412666307504015621 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: git-magic@lordofbikes.de\n" "POT-Creation-Date: 2011-07-03 23:06+0300\n" "PO-Revision-Date: 2010-11-01 22:14+0100\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/basic.txt:2 msgid "== Basic Tricks ==" msgstr "== Erste Schritte ==" #. type: Plain text #: ../en/basic.txt:6 msgid "" "Rather than diving into a sea of Git commands, use these elementary examples " "to get your feet wet. Despite their simplicity, each of them are useful. " "Indeed, in my first months with Git I never ventured beyond the material in " "this chapter." msgstr "" "Bevor wir uns in ein Meer von Git-Befehlen stürzen, schauen wir uns ein paar " "einfache Beispiele an. Trotz ihrer Einfachheit, sind alle davon wichtig und " "nützlich. Um ehrlich zu sein, meine ersten Monate mit Git brauchte ich nicht " "mehr als in diesem Kapitel beschrieben steht." #. type: Plain text #: ../en/basic.txt:8 msgid "=== Saving State ===" msgstr "=== Stand sichern ===" #. type: Plain text #: ../en/basic.txt:11 msgid "" "About to attempt something drastic? Before you do, take a snapshot of all " "files in the current directory with:" msgstr "" "Hast du gravierende Änderungen vor? Nur zu, aber speichere deinen aktuellen " "Stand vorher lieber nochmal ab:" #. type: Plain text #: ../en/basic.txt:15 #, no-wrap msgid "" " $ git init\n" " $ git add .\n" " $ git commit -m \"My first backup\"\n" msgstr "" " $ git init\n" " $ git add .\n" " $ git commit -m \"Meine erste Sicherung\"\n" #. type: Plain text #: ../en/basic.txt:17 msgid "Now if your new edits go awry, restore the pristine version:" msgstr "" "Falls deine Änderungen schief gehen, kannst du jetzt die alte Version " "wiederherstellen:" #. type: Plain text #: ../en/basic.txt:19 #, no-wrap msgid " $ git reset --hard\n" msgstr " $ git reset --hard\n" #. type: Plain text #: ../en/basic.txt:21 msgid "To save the state again:" msgstr "Um den neuen Stand zu sichern:" #. type: Plain text #: ../en/basic.txt:23 #, no-wrap msgid " $ git commit -a -m \"Another backup\"\n" msgstr " $ git commit -a -m \"Eine andere Sicherung\"\n" #. type: Plain text #: ../en/basic.txt:25 msgid "=== Add, Delete, Rename ===" msgstr "=== Hinzufügen, Löschen, Umbenennen ===" #. type: Plain text #: ../en/basic.txt:27 msgid "" "The above only keeps track of the files that were present when you first ran " "*git add*. If you add new files or subdirectories, you'll have to tell Git:" msgstr "" "Bisher kümmert sich Git nur um Dateien, die existierten, als du das erste " "Mal *git add* ausgeführt hast. Wenn du Dateien oder Verzeichnisse " "hinzufügst, musst du Git das mitteilen:" #. type: Plain text #: ../en/basic.txt:29 #, no-wrap msgid " $ git add readme.txt Documentation\n" msgstr " $ git add readme.txt Dokumentation\n" #. type: Plain text #: ../en/basic.txt:31 msgid "Similarly, if you want Git to forget about certain files:" msgstr "Ebenso, wenn Git Dateien vergessen soll:" #. type: Plain text #: ../en/basic.txt:34 #, no-wrap msgid "" " $ git rm kludge.h obsolete.c\n" " $ git rm -r incriminating/evidence/\n" msgstr "" " $ git rm ramsch.h veraltet.c\n" " $ git rm -r belastendes/material/\n" #. type: Plain text #: ../en/basic.txt:36 msgid "Git deletes these files for you if you haven't already." msgstr "Git löscht diese Dateien für dich, falls du es noch nicht getan hast." #. type: Plain text #: ../en/basic.txt:38 msgid "" "Renaming a file is the same as removing the old name and adding the new " "name. There's also the shortcut *git mv* which has the same syntax as the " "*mv* command. For example:" msgstr "" "Eine Datei umzubenennen ist das selbe wie sie zu löschen und unter neuem " "Namen hinzuzufügen. Git benutzt hierzu die Abkürzung *git mv*, welche die " "gleiche Syntax wie *mv* hat. Zum Beispiel:" #. type: Plain text #: ../en/basic.txt:40 #, no-wrap msgid " $ git mv bug.c feature.c\n" msgstr " $ git mv fehler.c feature.c\n" #. type: Plain text #: ../en/basic.txt:42 msgid "=== Advanced Undo/Redo ===" msgstr "=== Fortgeschrittenes Rückgängig machen/Wiederherstellen ===" #. type: Plain text #: ../en/basic.txt:44 msgid "" "Sometimes you just want to go back and forget about every change past a " "certain point because they're all wrong. Then:" msgstr "" "Manchmal möchtest du einfach zurück gehen und alle Änderungen ab einem " "bestimmten Zeitpunkt verwerfen, weil sie falsch waren. Dann:" #. type: Plain text #: ../en/basic.txt:46 #, no-wrap msgid " $ git log\n" msgstr " $ git log\n" #. type: Plain text #: ../en/basic.txt:48 msgid "shows you a list of recent commits, and their SHA1 hashes:" msgstr "" "zeigt dir eine Liste der bisherigen 'Commits' und deren SHA1 Hashwerte:" #. type: Plain text #: ../en/basic.txt:49 #, no-wrap msgid "----------------------------------\n" msgstr "----------------------------------\n" #. type: Plain text #: ../en/basic.txt:53 #, no-wrap msgid "" "commit 766f9881690d240ba334153047649b8b8f11c664\n" "Author: Bob \n" "Date: Tue Mar 14 01:59:26 2000 -0800\n" msgstr "" "commit 766f9881690d240ba334153047649b8b8f11c664\n" "Author: Bob \n" "Date: Tue Mar 14 01:59:26 2000 -0800\n" #. type: Plain text #: ../en/basic.txt:55 #, no-wrap msgid " Replace printf() with write().\n" msgstr " Ersetze printf() mit write().\n" #. type: Plain text #: ../en/basic.txt:59 #, no-wrap msgid "" "commit 82f5ea346a2e651544956a8653c0f58dc151275c\n" "Author: Alice \n" "Date: Thu Jan 1 00:00:00 1970 +0000\n" msgstr "" "commit 82f5ea346a2e651544956a8653c0f58dc151275c\n" "Author: Alice \n" "Date: Thu Jan 1 00:00:00 1970 +0000\n" #. type: Plain text #: ../en/basic.txt:61 #, no-wrap msgid "" " Initial commit.\n" "----------------------------------\n" msgstr "" " Initial commit.\n" "----------------------------------\n" #. type: Plain text #: ../en/basic.txt:65 msgid "" "The first few characters of the hash are enough to specify the commit; " "alternatively, copy and paste the entire hash. Type:" msgstr "" "Die ersten paar Zeichen eines Hashwert reichen aus um einen 'Commit' zu " "identifizieren; alternativ benutze kopieren und einfügen für den kompletten " "Hashwert. Gib ein:" #. type: Plain text #: ../en/basic.txt:67 #, no-wrap msgid " $ git reset --hard 766f\n" msgstr " $ git reset --hard 766f\n" #. type: Plain text #: ../en/basic.txt:69 msgid "" "to restore the state to a given commit and erase all newer commits from the " "record permanently." msgstr "" "um den Stand eines bestimmten 'Commits' wieder herzustellen und alle " "nachfolgenden Änderungen für immer zu löschen." #. type: Plain text #: ../en/basic.txt:71 msgid "" "Other times you want to hop to an old state briefly. In this case, type:" msgstr "" "Ein anderes Mal willst du nur kurz zu einem älteren Stand springen. In " "diesem Fall, gib folgendes ein:" #. type: Plain text #: ../en/basic.txt:73 #, no-wrap msgid " $ git checkout 82f5\n" msgstr " $ git checkout 82f5\n" #. type: Plain text #: ../en/basic.txt:75 msgid "" "This takes you back in time, while preserving newer commits. However, like " "time travel in a science-fiction movie, if you now edit and commit, you will " "be in an alternate reality, because your actions are different to what they " "were the first time around." msgstr "" "Damit springst du in der Zeit zurück, behältst aber neuere Änderungen. Aber, " "wie bei Zeitreisen in einem Science-Fiction-Film, wenn du jetzt etwas " "änderst und 'commitest', gelangst du in ein alternative Realität, denn deine " "Änderungen sind anders als beim früheren 'Commit'." #. type: Plain text #: ../en/basic.txt:77 msgid "" "This alternate reality is called a 'branch', and <>. For now, just remember that" msgstr "" "Diese alternative Realität heißt 'Branch' und <>. Für jetzt, merke dir" #. type: Plain text #: ../en/basic.txt:79 #, no-wrap msgid " $ git checkout master\n" msgstr " $ git checkout master\n" #. type: Plain text #: ../en/basic.txt:82 msgid "" "will take you back to the present. Also, to stop Git complaining, always " "commit or reset your changes before running checkout." msgstr "" "bringt dich wieder in die Gegenwart. Um zu verhindern, dass sich Git " "beschwert, solltest du vor einem 'Checkout' alle Änderungen 'commiten' oder " "'reseten'." #. type: Plain text #: ../en/basic.txt:84 msgid "To take the computer game analogy again:" msgstr "Um wieder die Computerspielanalogie anzuwenden:" #. type: Plain text #: ../en/basic.txt:86 msgid "" "- *`git reset --hard`*: load an old save and delete all saved games newer " "than the one just loaded." msgstr "" "- *`git reset --hard`*: Lade einen alten Stand und lösche alle Spielstände, " "die neuer sind als der jetzt geladene." #. type: Plain text #: ../en/basic.txt:88 msgid "" "- *`git checkout`*: load an old game, but if you play on, the game state " "will deviate from the newer saves you made the first time around. Any saved " "games you make now will end up in a separate branch representing the " "alternate reality you have entered. <>." msgstr "" "- *`git checkout`*: Lade einen alten Spielstand, aber wenn du weiterspielst, " "wird der Spielstand von den früher gesicherten Spielständen abweichen. Jeder " "Spielstand, der ab jetzt gesichert wird, entsteht in dem separaten 'Branch', " "welcher der alternative Realität entspricht. <>." #. type: Plain text #: ../en/basic.txt:90 msgid "" "You can choose only to restore particular files and subdirectories by " "appending them after the command:" msgstr "" "Du kannst auch nur einzelne Dateien oder Verzeichnisse wiederherstellen " "indem du sie an den Befehl anhängst:" #. type: Plain text #: ../en/basic.txt:92 #, no-wrap msgid " $ git checkout 82f5 some.file another.file\n" msgstr " $ git checkout 82f5 eine.datei andere.datei\n" #. type: Plain text #: ../en/basic.txt:96 msgid "" "Take care, as this form of *checkout* can silently overwrite files. To " "prevent accidents, commit before running any checkout command, especially " "when first learning Git. In general, whenever you feel unsure about any " "operation, Git command or not, first run *git commit -a*." msgstr "" "Sei Vorsichtig, diese Art des '*Checkout*' kann Dateien überschreiben, ohne " "dass du etwas merkst. Um Unfälle zu vermeiden solltest du immer 'commiten' " "bevor du ein 'Checkout' machst, besonders am Anfang wenn du Git noch " "erlernst. Allgemein gilt: Wenn du unsicher bist, egal ob ein Git Befehl oder " "irgendeine andere Operation, führe zuerst *git commit -a* aus." #. type: Plain text #: ../en/basic.txt:98 msgid "Don't like cutting and pasting hashes? Then use:" msgstr "Du magst Kopieren und Einfügen von Hashes nicht? Dann nutze:" #. type: Plain text #: ../en/basic.txt:100 #, no-wrap msgid " $ git checkout :/\"My first b\"\n" msgstr " $ git checkout :/\"Meine erste Si\"\n" #. type: Plain text #: ../en/basic.txt:103 msgid "" "to jump to the commit that starts with a given message. You can also ask " "for the 5th-last saved state:" msgstr "" "um zu einem 'Commit' zu springen, dessen Beschreibung so anfängt. Du kannst " "auch nach dem 5. letzten 'Commit' fragen:" #. type: Plain text #: ../en/basic.txt:105 #, no-wrap msgid " $ git checkout master~5\n" msgstr " $ git checkout master~5\n" #. type: Plain text #: ../en/basic.txt:107 msgid "=== Reverting ===" msgstr "=== Rückgängig machen ===" #. type: Plain text #: ../en/basic.txt:109 msgid "" "In a court of law, events can be stricken from the record. Likewise, you can " "pick specific commits to undo." msgstr "" "In einem Gerichtssaal können Ereignisse aus den Akten gelöscht werden. " "Ähnlich kannst du gezielt 'Commits' rückgängig machen." #. type: Plain text #: ../en/basic.txt:112 #, no-wrap msgid "" " $ git commit -a\n" " $ git revert 1b6d\n" msgstr "" " $ git commit -a\n" " $ git revert 1b6d\n" #. type: Plain text #: ../en/basic.txt:115 msgid "" "will undo just the commit with the given hash. The revert is recorded as a " "new commit, which you can confirm by running *git log*." msgstr "" "wird den 'Commit' mit dem angegebenen Hashwert rückgängig machen. Das " "Rückgängig machen wird als neuer 'Commit' erstellt, was mit *git log* " "überprüft werden kann." #. type: Plain text #: ../en/basic.txt:117 msgid "=== Changelog Generation ===" msgstr "=== Changelog erstellen ===" #. type: Plain text #: ../en/basic.txt:120 msgid "" "Some projects require a http://en.wikipedia.org/wiki/Changelog[changelog]. " "Generate one by typing:" msgstr "" "Verschiedene Projekte benötigen ein http://de.wikipedia.org/wiki/" "%C3%84nderungsprotokoll[Änderungsprotokoll]. Das kannst du mit folgendem " "Befehl erstellen:" #. type: Plain text #: ../en/basic.txt:122 #, no-wrap msgid " $ git log > ChangeLog\n" msgstr " $ git log > ChangeLog\n" #. type: Plain text #: ../en/basic.txt:124 msgid "=== Downloading Files ===" msgstr "=== Dateien herunterladen ===" #. type: Plain text #: ../en/basic.txt:126 msgid "Get a copy of a project managed with Git by typing:" msgstr "Eine Kopie eines mit Git verwalteten Projekts bekommst du mit:" #. type: Plain text #: ../en/basic.txt:128 #, no-wrap msgid " $ git clone git://server/path/to/files\n" msgstr " $ git clone git://server/pfad/zu/dateien\n" #. type: Plain text #: ../en/basic.txt:130 msgid "For example, to get all the files I used to create this site:" msgstr "" "Um zum Beispiel alle Dateien zu bekommen, die ich zum Erzeugen dieser Seiten " "benutze:" #. type: Plain text #: ../en/basic.txt:132 #, no-wrap msgid " $ git clone git://git.or.cz/gitmagic.git\n" msgstr " $ git clone git://git.or.cz/gitmagic.git\n" #. type: Plain text #: ../en/basic.txt:134 msgid "We'll have much to say about the *clone* command soon." msgstr "Es gibt gleich noch viel mehr über den 'clone' Befehl zu sagen." #. type: Plain text #: ../en/basic.txt:136 msgid "=== The Bleeding Edge ===" msgstr "=== Das Neueste vom Neuen ===" #. type: Plain text #: ../en/basic.txt:138 msgid "" "If you've already downloaded a copy of a project using *git clone*, you can " "upgrade to the latest version with:" msgstr "" "Wenn du schon eine Kopie eines Projektes hast, kannst du es auf die neuste " "Version aktualisieren mit:" #. type: Plain text #: ../en/basic.txt:140 ../en/basic.txt:166 #, no-wrap msgid " $ git pull\n" msgstr " $ git pull\n" #. type: Plain text #: ../en/basic.txt:142 msgid "=== Instant Publishing ===" msgstr "=== Einfaches Veröffentlichen ===" #. type: Plain text #: ../en/basic.txt:144 msgid "" "Suppose you've written a script you'd like to share with others. You could " "just tell them to download from your computer, but if they do so while " "you're improving the script or making experimental changes, they could wind " "up in trouble. Of course, this is why release cycles exist. Developers may " "work on a project frequently, but they only make the code available when " "they feel it is presentable." msgstr "" "Angenommen du hast ein Skript geschrieben und möchtest es anderen zugänglich " "machen. Du könntest sie einfach bitten es von deinem Computer " "herunterzuladen, aber falls sie das tun während du experimentierst oder das " "Skript verbesserst könnten sie in Schwierigkeiten geraten. Genau deswegen " "gibt es Releasezyklen. Entwickler arbeiten regelmäßig an einem Projekt, " "veröffentlichen den Code aber nur, wenn sie ihn für vorzeigbar halten." #. type: Plain text #: ../en/basic.txt:146 msgid "To do this with Git, in the directory where your script resides:" msgstr "Um dies in Git zu tun, gehe ins Verzeichnis in dem das Skript liegt:" #. type: Plain text #: ../en/basic.txt:150 #, no-wrap msgid "" " $ git init\n" " $ git add .\n" " $ git commit -m \"First release\"\n" msgstr "" " $ git init\n" " $ git add .\n" " $ git commit -m \"Erster Stand\"\n" #. type: Plain text #: ../en/basic.txt:152 msgid "Then tell your users to run:" msgstr "Dann sage deinen Nutzern:" #. type: Plain text #: ../en/basic.txt:154 #, no-wrap msgid " $ git clone your.computer:/path/to/script\n" msgstr " $ git clone dein.computer:/pfad/zum/skript\n" #. type: Plain text #: ../en/basic.txt:156 msgid "" "to download your script. This assumes they have ssh access. If not, run *git " "daemon* and tell your users to instead run:" msgstr "" "um dein Skript herunterzuladen. Das setzt voraus, dass sie einen SSH-Zugang " "haben. Falls nicht, führe *git deamon* aus und sage den Nutzern folgendes:" #. type: Plain text #: ../en/basic.txt:158 #, no-wrap msgid " $ git clone git://your.computer/path/to/script\n" msgstr " $ git clone git://dein.computer/pfad/zum/skript\n" #. type: Plain text #: ../en/basic.txt:160 msgid "From now on, every time your script is ready for release, execute:" msgstr "Ab jetzt, immer wenn dein Skript reif für eine Veröffentlichung ist:" #. type: Plain text #: ../en/basic.txt:162 #, no-wrap msgid " $ git commit -a -m \"Next release\"\n" msgstr " $ git commit -a -m \"Nächster Stand\"\n" #. type: Plain text #: ../en/basic.txt:164 msgid "" "and your users can upgrade their version by changing to the directory " "containing your script and typing:" msgstr "und deine Nutzer können ihr Skript aktualisieren mit:" #. type: Plain text #: ../en/basic.txt:168 msgid "" "Your users will never end up with a version of your script you don't want " "them to see." msgstr "" "Deine Nutzer werden nie mit Versionen in Kontakt kommen, von denen du es " "nicht willst. Natürlich funktioniert der Trick für fast alles, nicht nur " "Skripts." #. type: Plain text #: ../en/basic.txt:170 msgid "=== What Have I Done? ===" msgstr "=== Was habe ich getan? ===" #. type: Plain text #: ../en/basic.txt:172 msgid "Find out what changes you've made since the last commit with:" msgstr "Finde heraus was du seit dem letzten 'Commit' getan hast:" #. type: Plain text #: ../en/basic.txt:174 #, no-wrap msgid " $ git diff\n" msgstr " $ git diff\n" #. type: Plain text #: ../en/basic.txt:176 msgid "Or since yesterday:" msgstr "Oder seit Gestern:" #. type: Plain text #: ../en/basic.txt:178 #, no-wrap msgid " $ git diff \"@{yesterday}\"\n" msgstr " $ git diff \"@{gestern}\"\n" #. type: Plain text #: ../en/basic.txt:180 msgid "Or between a particular version and 2 versions ago:" msgstr "Oder zwischen irgendeiner Version und der vorvorletzten:" #. type: Plain text #: ../en/basic.txt:182 #, no-wrap msgid " $ git diff 1b6d \"master~2\"\n" msgstr " $ git diff 1b6d \"master~2\"\n" #. type: Plain text #: ../en/basic.txt:185 msgid "" "In each case the output is a patch that can be applied with *git apply*. " "Try also:" msgstr "" "Jedes mal ist die Ausgabe ein 'Patch' der mit *git apply* eingespielt werden " "kann. Versuche auch:" #. type: Plain text #: ../en/basic.txt:187 #, no-wrap msgid " $ git whatchanged --since=\"2 weeks ago\"\n" msgstr " $ git whatchanged --since=\"2 weeks ago\"\n" #. type: Plain text #: ../en/basic.txt:189 msgid "" "Often I'll browse history with http://sourceforge.net/projects/qgit[qgit] " "instead, due to its slick photogenic interface, or http://jonas.nitro.dk/tig/" "[tig], a text-mode interface that works well over slow connections. " "Alternatively, install a web server, run *git instaweb* and fire up any web " "browser." msgstr "" "Um mir die Geschichte eines 'Repositories' anzuzeigen benutze ich häufig " "http://sourceforge.net/projects/qgit[qgit] da es eine schicke " "Benutzeroberfläche hat, oder http://jonas.nitro.dk/tig/[tig], eine " "Konsolenanwendung, die sehr gut über langsame Verbindungen funktioniert. " "Alternativ kannst du einen Webserver installieren mit *git instaweb*, dann " "kannst du mit jedem Webbrowser darauf zugreifen." #. type: Plain text #: ../en/basic.txt:191 msgid "=== Exercise ===" msgstr "=== Übung ===" #. type: Plain text #: ../en/basic.txt:193 msgid "" "Let A, B, C, D be four successive commits where B is the same as A except " "some files have been removed. We want to add the files back at D. How can we " "do this?" msgstr "" "A, B, C, D sind 4 aufeinander folgende 'Commits'. B ist identisch mit A, " "außer dass einige Dateien gelöscht wurden. Wir möchten die Dateien in D " "wieder hinzufügen, aber nicht in B. Wie machen wir das?" #. type: Plain text #: ../en/basic.txt:195 msgid "There are at least three solutions. Assuming we are at D:" msgstr "Es gibt mindestens 3 Lösungen. Angenommen, wir sind bei D:" #. type: Bullet: ' 1. ' #: ../en/basic.txt:197 msgid "" "The difference between A and B are the removed files. We can create a patch " "representing this difference and apply it:" msgstr "" "Der Unterschied zwischen A und B sind die gelöschten Dateien. Wir können " "einen 'Patch' erstellen, der diesen Unterschied darstellt und diesen dann " "auf D anwenden:" #. type: Plain text #: ../en/basic.txt:199 #, no-wrap msgid " $ git diff B A | git apply\n" msgstr " $ git diff B A | git apply\n" #. type: Bullet: ' 2. ' #: ../en/basic.txt:201 msgid "Since we saved the files back at A, we can retrieve them:" msgstr "" "Da die Dateien im 'Repository' unter dem 'Commit' A gespeichert sind, können " "wir sie wieder herstellen:" #. type: Plain text #: ../en/basic.txt:203 #, no-wrap msgid " $ git checkout A foo.c bar.h\n" msgstr " $ git checkout A foo.c bar.h\n" #. type: Bullet: ' 3. ' #: ../en/basic.txt:205 msgid "We can view going from A to B as a change we want to undo:" msgstr "" "Wir können den 'Commit' von A auf B als Änderung betrachten, die wir " "rückgängig machen wollen:" #. type: Plain text #: ../en/basic.txt:207 #, no-wrap msgid " $ git revert B\n" msgstr " $ git revert B\n" #. type: Plain text #: ../en/basic.txt:208 msgid "" "Which choice is best? Whichever you prefer most. It is easy to get what you " "want with Git, and often there are many ways to get it." msgstr "" "Welche Lösung ist die beste? Die, welche dir am besten gefällt. Es ist " "einfach mit Git das zu bekommen was du willst und oft führen viele Wege zum " "Ziel." gitmagic-20160304/de/pot/clone.po0000644000175000017500000007234612666307504015646 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010, 2011. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: bennlynn@gmail.com\n" "POT-Creation-Date: 2011-07-03 23:06+0300\n" "PO-Revision-Date: 2011-07-04 18:21+0200\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/clone.txt:2 msgid "== Cloning Around ==" msgstr "== Rund ums 'Clonen' ==" #. type: Plain text #: ../en/clone.txt:4 msgid "" "In older version control systems, checkout is the standard operation to get " "files. You retrieve a bunch of files in a particular saved state." msgstr "" "In älteren Versionsverwaltungssystemen ist 'checkout' die Standardoperation " "um Dateien zu bekommen. Du bekommst einen Haufen Dateien eines bestimmten " "Sicherungsstands." #. type: Plain text #: ../en/clone.txt:6 msgid "" "In Git and other distributed version control systems, cloning is the " "standard operation. To get files, you create a 'clone' of the entire " "repository. In other words, you practically mirror the central server. " "Anything the main repository can do, you can do." msgstr "" "In Git und anderen verteilten Versionsverwaltungssystemen ist 'clone' die " "Standardaktion. Um Dateien zu bekommen, erstellst du einen 'Clone' des " "gesamten 'Repository'. Oder anders gesagt, du spiegelst den zentralen " "Server. Alles, was man mit dem zentralen 'Repository' tun kann, kannst du " "auch mit deinem 'Clone' tun." #. type: Plain text #: ../en/clone.txt:8 msgid "=== Sync Computers ===" msgstr "=== Computer synchronisieren ===" #. type: Plain text #: ../en/clone.txt:10 msgid "" "I can tolerate making tarballs or using *rsync* for backups and basic " "syncing. But sometimes I edit on my laptop, other times on my desktop, and " "the two may not have talked to each other in between." msgstr "" "Es ist akzeptabel, für Datensicherungen und einfaches Synchronisieren, mit " "'tarball' Archiven oder *rsync* zu arbeiten. Aber manchmal arbeite ich an " "meinem Laptop, dann an meinem Desktop-PC und die beiden haben sich " "inzwischen nicht austauschen können." #. type: Plain text #: ../en/clone.txt:12 msgid "" "Initialize a Git repository and commit your files on one machine. Then on " "the other:" msgstr "" "Erstelle ein Git 'Repository' und 'commite' deine Dateien auf dem einen " "Rechner. Dann auf dem anderen:" #. type: Plain text #: ../en/clone.txt:14 #, no-wrap msgid " $ git clone other.computer:/path/to/files\n" msgstr " $ git clone anderer.computer:/pfad/zu/dateien\n" #. type: Plain text #: ../en/clone.txt:16 msgid "to create a second copy of the files and Git repository. From now on," msgstr "" "um eine zweite Kopie der Dateien und des Git 'Repository' zu erstellen. Von " "jetzt an wird" #. type: Plain text #: ../en/clone.txt:19 #, no-wrap msgid "" " $ git commit -a\n" " $ git pull other.computer:/path/to/files HEAD\n" msgstr "" " $ git commit -a\n" " $ git pull anderer.computer:/pfad/zu/dateien HEAD\n" #. type: Plain text #: ../en/clone.txt:21 msgid "" "will 'pull' in the state of the files on the other computer into the one " "you're working on. If you've recently made conflicting edits in the same " "file, Git will let you know and you should commit again after resolving them." msgstr "" "den Zustand der Dateien des anderen Computer auf den übertragen, an dem du " "gerade arbeitest. Solltest du kürzlich konkurrierende Änderungen an der " "selben Datei vorgenommen haben, lässt Git dich das wissen und musst erneut " "'commiten' nachdem du die Konflikte aufgelöst hast." #. type: Plain text #: ../en/clone.txt:23 msgid "=== Classic Source Control ===" msgstr "=== Klassische Quellcodeverwaltung ===" #. type: Plain text #: ../en/clone.txt:25 msgid "Initialize a Git repository for your files:" msgstr "Erstelle ein Git 'Repository' für deine Dateien:" #. type: Plain text #: ../en/clone.txt:29 ../en/clone.txt:157 #, no-wrap msgid "" " $ git init\n" " $ git add .\n" " $ git commit -m \"Initial commit\"\n" msgstr "" " $ git init\n" " $ git add .\n" " $ git commit -m \"Erster Commit\"\n" #. type: Plain text #: ../en/clone.txt:31 msgid "" "On the central server, initialize a 'bare repository' in some directory:" msgstr "" "Auf dem zentralen Server erstelle ein 'bare Repository' in irgendeinem " "Ordner:" #. type: Plain text #: ../en/clone.txt:36 #, no-wrap msgid "" " $ mkdir proj.git\n" " $ cd proj.git\n" " $ git init --bare\n" " $ touch proj.git/git-daemon-export-ok\n" msgstr "" " $ mkdir proj.git\n" " $ cd proj.git\n" " $ git init --bare\n" " $ touch proj.git/git-daemon-export-ok\n" #. type: Plain text #: ../en/clone.txt:38 msgid "Start the Git daemon if necessary:" msgstr "Wenn nötig, starte den Git-Dämon:" #. type: Plain text #: ../en/clone.txt:40 #, no-wrap msgid " $ git daemon --detach # it may already be running\n" msgstr " $ git daemon --detach # er könnte schon laufen\n" #. type: Plain text #: ../en/clone.txt:43 msgid "" "For Git hosting services, follow the instructions to setup the initially " "empty Git repository. Typically one fills in a form on a webpage." msgstr "" "Für Git Hostingdienste folge den Anweisungen zum Erstellen des zunächst " "leeren Git 'Repository'. Normalerweise füllt man ein Formular auf einer " "Website aus." #. type: Plain text #: ../en/clone.txt:45 msgid "'Push' your project to the central server with:" msgstr "Übertrage ('push') dein Projekt auf den zentralen Server mit:" #. type: Plain text #: ../en/clone.txt:47 #, no-wrap msgid " $ git push central.server/path/to/proj.git HEAD\n" msgstr " $ git push zentraler.server/pfad/zu/proj.git HEAD\n" #. type: Plain text #: ../en/clone.txt:49 msgid "To check out the source, a developer types:" msgstr "Um die Quellcodes abzurufen gibt ein Entwickler folgendes ein:" #. type: Plain text #: ../en/clone.txt:51 #, no-wrap msgid " $ git clone central.server/path/to/proj.git\n" msgstr " $ git clone zentraler.server/pfad/zu/proj.git\n" #. type: Plain text #: ../en/clone.txt:53 msgid "After making changes, the developer saves changes locally:" msgstr "Nach dem Bearbeiten sichert der Entwickler die Änderungen lokal:" #. type: Plain text #: ../en/clone.txt:55 ../en/clone.txt:63 #, no-wrap msgid " $ git commit -a\n" msgstr " $ git commit -a\n" #. type: Plain text #: ../en/clone.txt:57 msgid "To update to the latest version:" msgstr "Um auf die aktuelle Server-Version zu aktualisieren:" #. type: Plain text #: ../en/clone.txt:59 ../en/clone.txt:126 #, no-wrap msgid " $ git pull\n" msgstr " $ git pull\n" #. type: Plain text #: ../en/clone.txt:61 msgid "Any merge conflicts should be resolved then committed:" msgstr "" "Irgendwelche 'Merge'-Konflikte sollten dann aufgelöst und erneut 'commitet' " "werden:" #. type: Plain text #: ../en/clone.txt:65 msgid "To check in local changes into the central repository:" msgstr "Um die lokalen Änderungen in das zentrale 'Repository' zu übertragen:" #. type: Plain text #: ../en/clone.txt:67 #, no-wrap msgid " $ git push\n" msgstr " $ git push\n" #. type: Plain text #: ../en/clone.txt:70 msgid "" "If the main server has new changes due to activity by other developers, the " "push fails, and the developer should pull the latest version, resolve any " "merge conflicts, then try again." msgstr "" "Wenn inzwischen neue Änderungen von anderen Entwicklern beim Hauptserver " "eingegangen sind, schlägt dein 'push' fehl. Aktualisiere das lokale " "'Repository' erneut mit 'pull', löse eventuell aufgetretene 'Merge'-" "Konflikte und versuche es nochmal." #. type: Plain text #: ../en/clone.txt:73 msgid "" "Developers must have SSH access for the above pull and push commands. " "However, anyone can see the source by typing:" msgstr "" "Entwickler brauchen SSH Zugriff für die vorherigen 'pull' und 'push' " "Anweisungen. Trotzdem kann jedermann die Quelltexte einsehen, durch Eingabe " "von:" #. type: Plain text #: ../en/clone.txt:75 #, no-wrap msgid " $ git clone git://central.server/path/to/proj.git\n" msgstr " $ git clone git://zentraler.server/pfad/zu/proj.git\n" #. type: Plain text #: ../en/clone.txt:79 msgid "" "The native git protocol is like HTTP: there is no authentication, so anyone " "can retrieve the project. Accordingly, by default, pushing is forbidden via " "the git protocol." msgstr "" "Das ursprüngliche Git-Protokoll ähnelt HTTP: Es gibt keine " "Authentifizierung, also kann jeder das Projekt abrufen. Folglich ist " "standardmäßig das 'Pushen' per Git-Protokoll verboten." #. type: Plain text #: ../en/clone.txt:81 msgid "=== Secret Source ===" msgstr "=== Geheime Quellen ===" #. type: Plain text #: ../en/clone.txt:87 msgid "" "For a closed-source project, omit the touch command, and ensure you never " "create a file named `git-daemon-export-ok`. The repository can no longer be " "retrieved via the git protocol; only those with SSH access can see it. If " "all your repos are closed, running the git daemon is unnecessary because all " "communication occurs via SSH." msgstr "" "Für ein Closed-Source-Projekt lasse die 'touch' Anweisung weg und stelle " "sicher, dass niemals eine Datei namens `git-daemon-export-ok` erstellt wird. " "Das 'Repository' kann nun nicht mehr über das Git-Protokol abgerufen werden; " "nur diejenigen mit SSH Zugriff können es einsehen. Wenn alle 'Repositories' " "geschlossen sind, ist es unnötig den Git Dämon laufen zu lassen, da jegliche " "Kommunikation über SSH läuft." #. type: Plain text #: ../en/clone.txt:89 msgid "=== Bare repositories ===" msgstr "=== 'Nackte Repositories' ===" #. type: Plain text #: ../en/clone.txt:91 msgid "" "A bare repository is so named because it has no working directory; it only " "contains files that are normally hidden away in the `.git` subdirectory. In " "other words, it maintains the history of a project, and never holds a " "snapshot of any given version." msgstr "" "Ein nacktes ('bare') 'Repository' wird so genannt, weil es kein " "Arbeitsverzeichnis hat. Es enthält nur Dateien, die normalerweise im '.git' " "Unterverzeichnis versteckt sind. Mit anderen Worten, es verwaltet die " "Geschichte eines Projekts, enthält aber niemals einen Auszug irgendeiner " "beliebigen Version." #. type: Plain text #: ../en/clone.txt:98 msgid "" "A bare repository plays a role similar to that of the main server in a " "centralized version control system: the home of your project. Developers " "clone your project from it, and push the latest official changes to it. " "Typically it resides on a server that does little else but disseminate data. " "Development occurs in the clones, so the home repository can do without a " "working directory." msgstr "" "Ein 'bare Repository' übernimmt die Rolle des Hauptserver in einem " "zentralisierten Versionsverwaltungssystem: Das Zuhause deines Projekts. " "Entwickler 'clonen' dein Projekt davon und 'pushen' die letzten offiziellen " "Änderungen dort hin. Meistens befindet es sich auf einem Server, der nicht " "viel tut außer Daten zu verbreiten. Die Entwicklung findet in den 'Clonen' " "statt, so kann das Heim-'Repository' ohne Arbeitsverzeichnis auskommen." #. type: Plain text #: ../en/clone.txt:100 msgid "" "Many Git commands fail on bare repositories unless the `GIT_DIR` environment " "variable is set to the repository path, or the `--bare` option is supplied." msgstr "" "Viele Git Befehle funktionieren nicht in 'bare Repositories'. Es sei denn " "die `GIT_DIR` Umgebungsvariable wird auf das Arbeitsverzeichnis gesetzt, " "oder die `--bare` Option wird übergeben." #. type: Plain text #: ../en/clone.txt:102 msgid "=== Push versus pull ===" msgstr "=== 'Push' oder 'Pull' ===" #. type: Plain text #: ../en/clone.txt:110 msgid "" "Why did we introduce the push command, rather than rely on the familiar pull " "command? Firstly, pulling fails on bare repositories: instead you must " "'fetch', a command we later discuss. But even if we kept a normal repository " "on the central server, pulling into it would still be cumbersome. We would " "have to login to the server first, and give the pull command the network " "address of the machine we're pulling from. Firewalls may interfere, and what " "if we have no shell access to the server in the first place?" msgstr "" "Warum haben wir den 'push'-Befehl eingeführt, anstatt bei dem vertrauten " "'pull'-Befehl zu bleiben? Zuerst, 'pull' funktioniert nicht mit 'bare " "Repositories': stattdessen benutze 'fetch', ein Befehl, den wir später " "behandeln. Aber auch wenn wir ein normales 'Repository' auf dem zentralen " "Server halten würden, wäre das 'pullen' eine mühselige Angelegenheit. Wir " "müssten uns zuerst in den Server einloggen und dem 'pull'-Befehl die " "Netzwerkadresse des Computer übergeben, von dem aus wir die Änderungen " "'pullen', also abholen wollen. Firewalls könnten uns stören und was, wenn " "wir gar keine Berechtigung für eine Serverkonsole haben." #. type: Plain text #: ../en/clone.txt:112 msgid "" "However, apart from this case, we discourage pushing into a repository, " "because confusion can ensue when the destination has a working directory." msgstr "" "Wie auch immer, abgesehen von diesem Fall, raten wir vom 'Pushen' in ein " "'Repository' ab. Falls das Ziel nämlich ein Arbeitsverzeichnis hat, können " "Verwirrungen entstehen." #. type: Plain text #: ../en/clone.txt:114 msgid "" "In short, while learning Git, only push when the target is a bare " "repository; otherwise pull." msgstr "" "Kurzum, während du lernst mit Git umzugehen, 'pushe' nur, wenn das Ziel ein " "'bare Repository' ist; andernfalls benutze 'pull'." #. type: Plain text #: ../en/clone.txt:116 msgid "=== Forking a Project ===" msgstr "=== 'Fork' eines Projekts ===" #. type: Plain text #: ../en/clone.txt:118 msgid "" "Sick of the way a project is being run? Think you could do a better job? " "Then on your server:" msgstr "" "Hast du es satt, wie sich ein Projekt entwickelt? Du denkst, du kannst das " "besser? Dann mache folgendes auf deinem Server:" #. type: Plain text #: ../en/clone.txt:120 #, no-wrap msgid " $ git clone git://main.server/path/to/files\n" msgstr " $ git clone git://haupt.server/pfad/zu/dateien\n" #. type: Plain text #: ../en/clone.txt:122 msgid "Next, tell everyone about your fork of the project at your server." msgstr "Dann erzähle jedem von deiner 'Fork' des Projekts auf deinem Server." #. type: Plain text #: ../en/clone.txt:124 msgid "" "At any later time, you can merge in the changes from the original project " "with:" msgstr "" "Zu jedem späteren Zeitpunkt kannst du die Änderungen des Originalprojekts " "'mergen' mit: " #. type: Plain text #: ../en/clone.txt:128 msgid "=== Ultimate Backups ===" msgstr "=== Ultimative Datensicherung ===" #. type: Plain text #: ../en/clone.txt:130 msgid "" "Want numerous tamper-proof geographically diverse redundant archives? If " "your project has many developers, don't do anything! Every clone of your " "code is effectively a backup. Not just of the current state, but of your " "project's entire history. Thanks to cryptographic hashing, if anyone's clone " "becomes corrupted, it will be spotted as soon as they try to communicate " "with others." msgstr "" "Du willst zahlreiche, vor Manipulation geschützte, redundante " "Datensicherungen an unterschiedlichen Orten? Wenn dein Projekt viele " "Entwickler hat, musst du nichts tun! Jeder 'Clone' deines Codes ist eine " "vollwertige Datensicherung. Nicht nur des aktuellen Stand, sondern der " "gesamten Geschichte. Wird irgendein 'Clone' beschädigt, wird dies dank des " "kryptographischen 'Hashing' sofort erkannt, sobald derjenige versucht mit " "anderen zu kommunizieren." #. type: Plain text #: ../en/clone.txt:132 msgid "" "If your project is not so popular, find as many servers as you can to host " "clones." msgstr "" "Wenn dein Projekt nicht so bekannt ist, finde so viele Server wie du kannst " "um dort einen 'Clone' zu platzieren." #. type: Plain text #: ../en/clone.txt:134 msgid "" "The truly paranoid should always write down the latest 20-byte SHA1 hash of " "the HEAD somewhere safe. It has to be safe, not private. For example, " "publishing it in a newspaper would work well, because it's hard for an " "attacker to alter every copy of a newspaper." msgstr "" "Die wirklich Paranoiden sollten immer den letzten 20-Byte SHA1 Hash des " "'HEAD' aufschreiben und an einem sicheren Ort aufbewahren. Er muss sicher " "sein, aber nicht privat. Zum Beispiel wäre es sicher, ihn in einer Zeitung " "zu veröffentlichen, denn es ist schwer für einen Angreifer jede " "Zeitungskopie zu manipulieren." #. type: Plain text #: ../en/clone.txt:136 msgid "=== Light-Speed Multitask ===" msgstr "=== Multitasking mit Lichtgeschwindigkeit ===" #. type: Plain text #: ../en/clone.txt:138 msgid "" "Say you want to work on several features in parallel. Then commit your " "project and run:" msgstr "" "Nehmen wir an du willst parallel an mehreren Funktionen arbeiten. Dann " "'commite' dein Projekt und gib ein:" #. type: Plain text #: ../en/clone.txt:140 ../en/clone.txt:161 #, no-wrap msgid " $ git clone . /some/new/directory\n" msgstr " $ git clone . /irgendein/neuer/ordner\n" #. type: Plain text #: ../en/clone.txt:143 msgid "" "Thanks to http://en.wikipedia.org/wiki/Hard_link[hardlinking], local clones " "require less time and space than a plain backup." msgstr "" "http://de.wikipedia.org/wiki/Harter_Link[Harten Links] ist es zu verdanken, " "dass ein lokaler Klon weniger Zeit und Speicherplatz benötigt als eine " "herkömmliche Datensicherung." #. type: Plain text #: ../en/clone.txt:147 msgid "" "You can now work on two independent features simultaneously. For example, " "you can edit one clone while the other is compiling. At any time, you can " "commit and pull changes from the other clone:" msgstr "" "Du kannst nun an zwei unabhängigen Funktionen gleichzeitig arbeiten. Zum " "Beispiel kannst Du einen Klon bearbeiten, während der andere kompiliert " "wird. Zu jeder Zeit kannst Du 'comitten' und die Änderungen des anderen Klon " "'pullen'." #. type: Plain text #: ../en/clone.txt:149 #, no-wrap msgid " $ git pull /the/other/clone HEAD\n" msgstr " $ git pull /der/andere/clone HEAD\n" #. type: Plain text #: ../en/clone.txt:151 msgid "=== Guerilla Version Control ===" msgstr "=== Versionsverwaltung im Untergrund ===" #. type: Plain text #: ../en/clone.txt:153 msgid "" "Are you working on a project that uses some other version control system, " "and you sorely miss Git? Then initialize a Git repository in your working " "directory:" msgstr "" "Arbeitest du an einem Projekt, das ein anderes Versionsverwaltungssystem " "nutzt und vermisst du Git? Dann erstelle ein Git 'Repository' in deinem " "Arbeitsverzeichnis:" #. type: Plain text #: ../en/clone.txt:159 msgid "then clone it:" msgstr "dann 'Clone' es:" #. type: Plain text #: ../en/clone.txt:163 msgid "" "Now go to the new directory and work here instead, using Git to your heart's " "content. Once in a while, you'll want to sync with everyone else, in which " "case go to the original directory, sync using the other version control " "system, and type:" msgstr "" "Nun gehe in das neue Verzeichnis und arbeite dort mit Git nach Herzenslust. " "Irgendwann wirst du dann mit den anderen synchronisieren wollen, dann gehe " "in das Originalverzeichnis, aktualisiere mit dem anderen " "Versionsverwaltungssystem und gib ein:" #. type: Plain text #: ../en/clone.txt:166 #, no-wrap msgid "" " $ git add .\n" " $ git commit -m \"Sync with everyone else\"\n" msgstr "" " $ git add .\n" " $ git commit -m \"Synchronisation mit den anderen\"\n" #. type: Plain text #: ../en/clone.txt:168 msgid "Then go to the new directory and run:" msgstr "Dann gehe wieder ins neue Verzeichnis und gib ein:" #. type: Plain text #: ../en/clone.txt:171 #, no-wrap msgid "" " $ git commit -a -m \"Description of my changes\"\n" " $ git pull\n" msgstr "" " $ git commit -a -m \"Beschreibung der Änderungen\"\n" " $ git pull\n" #. type: Plain text #: ../en/clone.txt:173 msgid "" "The procedure for giving your changes to everyone else depends on the other " "version control system. The new directory contains the files with your " "changes. Run whatever commands of the other version control system are " "needed to upload them to the central repository." msgstr "" "Die Vorgehensweise, wie du deine Änderungen den anderen übergibst, hängt vom " "anderen Versionsverwaltungssystem ab. Das neue Verzeichnis enthält die " "Dateien mit deinen Änderungen. Führe die Anweisungen des anderen " "Versionsverwaltungssystems aus, die nötig sind um die Dateien ins zentrale " "'Repository' zu übertragen." #. type: Plain text #: ../en/clone.txt:175 msgid "" "Subversion, perhaps the best centralized version control system, is used by " "countless projects. The *git svn* command automates the above for Subversion " "repositories, and can also be used to http://google-opensource.blogspot." "com/2008/05/export-git-project-to-google-code.html[export a Git project to a " "Subversion repository]." msgstr "" "Subversion, vielleicht das beste zentralisierte Versionsverwaltungssystem, " "wird von unzähligen Projekten benutzt. Der *git svn*-Befehl automatisiert " "den zuvor genannten Ablauf für Subversion 'Repositories' und kann auch " "benutzt werden um http://google-opensource.blogspot.com/2008/05/export-git-" "project-to-google-code.html[ein Git Projekt in ein Subversion 'Repository' " "zu exportieren]." #. type: Plain text #: ../en/clone.txt:177 msgid "=== Mercurial ===" msgstr "=== Mercurial ===" #. type: Plain text #: ../en/clone.txt:179 msgid "" "Mercurial is a similar version control system that can almost seamlessly " "work in tandem with Git. With the `hg-git` plugin, a Mercurial user can " "losslessly push to and pull from a Git repository." msgstr "" "Mercurial ist ein ähnliches Versionsverwaltungssystem, das fast nahtlos mit " "Git zusammenarbeiten kann. Mit der `hg-git`-Erweiterung kann ein Benutzer " "von Mercurial verlustfrei in ein Git 'Repository' 'pushen' und daraus " "'pullen'." #. type: Plain text #: ../en/clone.txt:181 msgid "Obtain the `hg-git` plugin with Git:" msgstr "Beschaffe dir die `hg-git`-Erweiterung mit Git:" #. type: Plain text #: ../en/clone.txt:183 #, no-wrap msgid " $ git clone git://github.com/schacon/hg-git.git\n" msgstr " $ git clone git://github.com/schacon/hg-git.git\n" #. type: Plain text #: ../en/clone.txt:185 msgid "or Mercurial:" msgstr "oder Mercurial:" #. type: Plain text #: ../en/clone.txt:187 #, no-wrap msgid " $ hg clone http://bitbucket.org/durin42/hg-git/\n" msgstr " $ hg clone http://bitbucket.org/durin42/hg-git/\n" #. type: Plain text #: ../en/clone.txt:189 msgid "" "Sadly, I am unaware of an analogous plugin for Git. For this reason, I " "advocate Git over Mercurial for the main repository, even if you prefer " "Mercurial. With a Mercurial project, usually a volunteer maintains a " "parallel Git repository to accommodate Git users, whereas thanks to the `hg-" "git` plugin, a Git project automatically accommodates Mercurial users." msgstr "" "Leider kenne ich keine solche Erweiterung für Git. Aus diesem Grund plädiere " "ich für Git statt Mercurial für ein zentrales 'Repository', auch wenn man " "Mercurial bevorzugt. Bei einem Mercurial Projekt gibt es gewöhnlich immer " "einen Freiwilligen, der parallel dazu ein Git 'Repository' für die Git " "Anwender unterhält, wogegen, Dank der `hg-git`-Erweiterung, ein Git Projekt " "automatisch die Benutzer von Mercurial mit einbezieht." #. type: Plain text #: ../en/clone.txt:191 msgid "" "Although the plugin can convert a Mercurial repository to a Git repository " "by pushing to an empty repository, this job is easier with the `hg-fast-" "export.sh` script, available from:" msgstr "" "Die Erweiterung kann auch ein Mercurial 'Repository' in ein Git 'Repository' " "umwandeln, indem man in ein leeres 'Repository' 'pushed'. Einfacher geht das " "mit dem `hg-fast-export.sh` Skript, welches es hier gibt:" #. type: Plain text #: ../en/clone.txt:193 #, no-wrap msgid " $ git clone git://repo.or.cz/fast-export.git\n" msgstr " $ git clone git://repo.or.cz/fast-export.git\n" #. type: Plain text #: ../en/clone.txt:195 msgid "To convert, in an empty directory:" msgstr "Zum Konvertieren gib in einem leeren Verzeichnis ein:" #. type: Plain text #: ../en/clone.txt:198 #, no-wrap msgid "" " $ git init\n" " $ hg-fast-export.sh -r /hg/repo\n" msgstr "" " $ git init\n" " $ hg-fast-export.sh -r /hg/repo\n" #. type: Plain text #: ../en/clone.txt:200 msgid "after adding the script to your `$PATH`." msgstr "nachdem du das Skript zu deinem `$PATH` hinzugefügt hast." #. type: Plain text #: ../en/clone.txt:202 msgid "=== Bazaar ===" msgstr "=== Bazaar ===" #. type: Plain text #: ../en/clone.txt:205 msgid "" "We briefly mention Bazaar because it is the most popular free distributed " "version control system after Git and Mercurial." msgstr "" "Wir erwähnen auch kurz Bazaar, weil es nach Git und Mercurial das " "bekannteste freie verteilte Versionsverwaltungssystem ist." #. type: Plain text #: ../en/clone.txt:207 msgid "" "Bazaar has the advantage of hindsight, as it is relatively young; its " "designers could learn from mistakes of the past, and sidestep minor " "historical warts. Additionally, its developers are mindful of portability " "and interoperation with other version control systems." msgstr "" "Bazaar hat den Vorteil des Rückblicks, da es relativ jung ist; seine " "Entwickler konnten aus Fehlern der Vergangenheit lernen und kleine " "historische Unwegbarkeiten umgehen. Außerdem waren sich die Entwickler der " "Popularität und Interoperabilität mit anderen Versionsverwaltungssystemen " "bewusst." #. type: Plain text #: ../en/clone.txt:209 msgid "" "A `bzr-git` plugin lets Bazaar users work with Git repositories to some " "extent. The `tailor` program converts Bazaar repositories to Git " "repositories, and can do so incrementally, while `bzr-fast-export` is well-" "suited for one-shot conversions." msgstr "" "Eine `bzr-git`-Erweiterung lässt Anwender von Bazaar einigermaßen mit Git " "'Repositories' arbeiten. Das `tailor` Programm konvertiert Bazaar " "'Repositories' zu Git 'Repositories' und kann das forlaufend tun, während " "`bzr-fast-export` für einmalige Konvertierungen besser geeignet ist." #. type: Plain text #: ../en/clone.txt:211 msgid "=== Why I use Git ===" msgstr "=== Warum ich Git benutze ===" #. type: Plain text #: ../en/clone.txt:213 msgid "" "I originally chose Git because I heard it could manage the unimaginably " "unmanageable Linux kernel source. I've never felt a need to switch. Git has " "served admirably, and I've yet to be bitten by its flaws. As I primarily use " "Linux, issues on other platforms are of no concern." msgstr "" "Ich habe ursprünglich Git gewählt, weil ich gehört habe, dass es die " "unvorstellbar unüberschaubaren Linux Kernel Quellcodes verwalten kann. Ich " "hatte noch keinen Grund zu wechseln. Git hat mir bewundernswert gedient und " "hat mich bis jetzt noch nie im Stich gelassen. Da ich in erster Linie unter " "Linux arbeite, sind Probleme anderer Plattformen bedeutungslos." #. type: Plain text #: ../en/clone.txt:215 msgid "" "Also, I prefer C programs and bash scripts to executables such as Python " "scripts: there are fewer dependencies, and I'm addicted to fast running " "times." msgstr "" "Ich bevorzuge auch C-Programme und 'bash'-Skripte gegenüber Anwendungen wie " "zum Beispiel Python Skripts: Es gibt weniger Abhängigkeiten und ich bin " "süchtig nach schellen Ausführungszeiten." #. type: Plain text #: ../en/clone.txt:217 msgid "" "I did think about how Git could be improved, going so far as to write my own " "Git-like tool, but only as an academic exercise. Had I completed my project, " "I would have stayed with Git anyway, as the gains are too slight to justify " "using an oddball system." msgstr "" "Ich dachte darüber nach, wie Git verbessert werden könnte, ging sogar so " "weit, dass ich meine eigene Git-Ähnliche Anwendung schrieb, allerdings nur " "als akademische Übungen. Hätte ich mein Projekt fertig gestellt, wäre ich " "trotzdem bei Git geblieben, denn die Verbesserungen wären zu gering gewesen " "um den Einsatz eines Eigenbrödler-Systems zu rechtfertigen." #. type: Plain text #: ../en/clone.txt:218 msgid "" "Naturally, your needs and wants likely differ, and you may be better off " "with another system. Nonetheless, you can't go far wrong with Git." msgstr "" "Natürlich können deine Bedürfnisse und Wünsche ganz anders sein und " "vielleicht bist du mit einem anderen System besser dran. Wie auch immer, mit " "Git kannst du nicht viel falsch machen." #~ msgid "" #~ "Git exploits hard links and file sharing as much as safely possible to " #~ "create this clone, so it will be ready in a flash, and you can now work " #~ "on two independent features simultaneously. For example, you can edit one " #~ "clone while the other is compiling." #~ msgstr "" #~ "Wann immer es sicher ist nutzt Git Verknüpfungen und Dateifreigaben um " #~ "diesen 'Clone' zu erzeugen, deshalb wird es blitzartig fertig sein und du " #~ "kannst nun an zwei unabhängigen Funktionen arbeiten. Zum Beispiel kannst " #~ "du im einen 'Clone' editieren, während der andere übersetzt wird." #~ msgid "At any time, you can commit and pull changes from the other clone." #~ msgstr "" #~ "Zu jeder Zeit kannst du 'commiten' und die Änderungen vom anderen 'Clone' " #~ "mit 'pull' übernehmen." gitmagic-20160304/de/pot/grandmaster.po0000644000175000017500000007174612666307504017060 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010, 2011. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: bennlynn@gmail.com\n" "POT-Creation-Date: 2010-10-30 08:21+0300\n" "PO-Revision-Date: 2011-07-07 19:00+0200\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/grandmaster.txt:2 msgid "== Git Grandmastery ==" msgstr "== Git für Fortgeschrittene ==" #. type: Plain text #: ../en/grandmaster.txt:7 msgid "" "By now, you should be able to navigate the *git help* pages and understand " "almost everything. However, pinpointing the exact command required to solve " "a given problem can be tedious. Perhaps I can save you some time: below are " "some recipes I have needed in the past." msgstr "" "Mittlerweile solltest Du Dich in den *git help* Seiten zurechtfinden und das " "meiste verstanden haben. Trotzdem kann es langwierig sein, den exakten " "Befehl zur Lösung einer bestimmten Aufgabe herauszufinden. Vielleicht kann " "ich Dir etwas Zeit sparen: Nachfolgend findest Du ein paar Rezepte, die ich " "in der Vergangenheit gebraucht habe." #. type: Plain text #: ../en/grandmaster.txt:9 msgid "=== Source Releases ===" msgstr "=== Quellcode veröffentlichen ===" #. type: Plain text #: ../en/grandmaster.txt:12 msgid "" "For my projects, Git tracks exactly the files I'd like to archive and " "release to users. To create a tarball of the source code, I run:" msgstr "" "Bei meinen Projekten verwaltet Git genau die Dateien, die ich archivieren " "und für andere Benutzer veröffentlichen will. Um ein tarball-Archiv des " "Quellcodes zu erzeugen, verwende ich den Befehl:" #. type: Plain text #: ../en/grandmaster.txt:14 #, no-wrap msgid " $ git archive --format=tar --prefix=proj-1.2.3/ HEAD\n" msgstr " $ git archive --format=tar --prefix=proj-1.2.3/ HEAD\n" #. type: Plain text #: ../en/grandmaster.txt:16 msgid "=== Commit What Changed ===" msgstr "=== 'Commite' Änderungen ===" #. type: Plain text #: ../en/grandmaster.txt:19 msgid "" "Telling Git when you've added, deleted and renamed files is troublesome for " "certain projects. Instead, you can type:" msgstr "" "Git mitzuteilen, welche Dateien man hinzugefügt, gelöscht und umbenannt hat, " "ist für manche Projekte sehr mühsam. Stattdessen kann man folgendes eingeben:" #. type: Plain text #: ../en/grandmaster.txt:22 #, no-wrap msgid "" " $ git add .\n" " $ git add -u\n" msgstr "" " $ git add .\n" " $ git add -u\n" #. type: Plain text #: ../en/grandmaster.txt:27 msgid "" "Git will look at the files in the current directory and work out the details " "by itself. Instead of the second add command, run `git commit -a` if you " "also intend to commit at this time. See *git help ignore* for how to specify " "files that should be ignored." msgstr "" "Git wird sich die Dateien im aktuellen Verzeichnis ansehen und sich die " "Details selbst erarbeiten. Anstelle des zweiten Befehl kann man auch `git " "commit -a` ausführen, falls man an dieser Stelle ohnehin 'comitten' möchte. " "Siehe *git help ignore* um zu sehen, wie man Dateien definiert, die " "ignoriert werden sollen." #. type: Plain text #: ../en/grandmaster.txt:29 msgid "You can perform the above in a single pass with:" msgstr "Man kann das aber auch in einem einzigen Schritt ausführen mit:" #. type: Plain text #: ../en/grandmaster.txt:31 #, no-wrap msgid " $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove\n" msgstr " $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove\n" #. type: Plain text #: ../en/grandmaster.txt:35 msgid "" "The *-z* and *-0* options prevent ill side-effects from filenames containing " "strange characters. As this command adds ignored files, you may want to use " "the `-x` or `-X` option." msgstr "" "Die *-z* und *-0* Optionen verhindern unerwünschte Nebeneffekte durch " "Dateinamen mit ungewöhnlichen Zeichen. Da diese Anweisung aber auch zu " "ignorierende Dateien hinzufügt, kann man noch die `-x` oder `-X` Option " "hinzufügen." #. type: Plain text #: ../en/grandmaster.txt:37 msgid "=== My Commit Is Too Big! ===" msgstr "=== Mein 'Commit' ist zu groß! ===" #. type: Plain text #: ../en/grandmaster.txt:41 msgid "" "Have you neglected to commit for too long? Been coding furiously and " "forgotten about source control until now? Made a series of unrelated " "changes, because that's your style?" msgstr "" "Hast Du es zu lange versäumt zu 'comitten'? Hast Du so versessen " "programmiert, daß Du darüber die Quellcodeverwaltung vergessen hast? Machst " "Du eine Serie von unabhängigen Änderungen, weil es Dein Stil ist?" #. type: Plain text #: ../en/grandmaster.txt:43 msgid "No worries. Run:" msgstr "Keine Sorge, gib ein:" #. type: Plain text #: ../en/grandmaster.txt:45 #, no-wrap msgid " $ git add -p\n" msgstr " $ git add -p\n" #. type: Plain text #: ../en/grandmaster.txt:49 msgid "" "For each edit you made, Git will show you the hunk of code that was changed, " "and ask if it should be part of the next commit. Answer with \"y\" or \"n\". " "You have other options, such as postponing the decision; type \"?\" to learn " "more." msgstr "" "Für jede Änderung, die Du gemacht hast, zeigt Git Dir die Codepassagen, die " "sich geändert haben und fragt ob sie Teil des nächsten 'Commit' sein sollen. " "Antworte mit \"y\" für Ja oder \"n\" für Nein. Du hast auch noch andere " "Optionen, z.B. den Aufschub der Entscheidung; drücke \"?\" um mehr zu " "erfahren." #. type: Plain text #: ../en/grandmaster.txt:51 msgid "Once you're satisfied, type" msgstr "Wenn Du zufrieden bist, gib" #. type: Plain text #: ../en/grandmaster.txt:53 #, no-wrap msgid " $ git commit\n" msgstr " $ git commit\n" #. type: Plain text #: ../en/grandmaster.txt:56 msgid "" "to commit precisely the changes you selected (the 'staged' changes). Make " "sure you omit the *-a* option, otherwise Git will commit all the edits." msgstr "" "ein um exakt die ausgewählten Änderungen zu 'comitten' (die \"inszenierten\" " "Änderungen). Achte darauf, nicht die Option *-a* einzusetzen, anderenfalls " "wird Git alle Änderungen 'comitten'." #. type: Plain text #: ../en/grandmaster.txt:63 msgid "" "What if you've edited many files in many places? Reviewing each change one " "by one becomes frustratingly mind-numbing. In this case, use *git add -i*, " "whose interface is less straightforward, but more flexible. With a few " "keystrokes, you can stage or unstage several files at a time, or review and " "select changes in particular files only. Alternatively, run *git commit \\--" "interactive* which automatically commits after you're done." msgstr "" "Was ist, wenn Du viele Dateien an verschiedenen Orten bearbeitet hast? Jede " "Datei einzeln nachzuprüfen ist frustrierend und ermüdend. In diesem Fall " "verwende *git add -i*, dessen Bedienung ist nicht ganz einfach, dafür aber " "sehr flexibel. Mit ein paar Tastendrücken kannst Du mehrere geänderte " "Dateien für den 'Commit' hinzufügen ('stage') oder entfernen ('unstage') " "oder Änderungen einzelner Dateien nachprüfen und hinzufügen. Alternativ " "kannst Du *git commit \\--interactive* verwenden, was dann automatisch die " "ausgewählten Änderungen 'commited' nachdem Du fertig bist." #. type: Plain text #: ../en/grandmaster.txt:65 msgid "=== The Index: Git's Staging Area ===" msgstr "=== Der Index: Git's Bereitstellungsraum ===" #. type: Plain text #: ../en/grandmaster.txt:71 msgid "" "So far we have avoided Git's famous 'index', but we must now confront it to " "explain the above. The index is a temporary staging area. Git seldom " "shuttles data directly between your project and its history. Rather, Git " "first writes data to the index, and then copies the data in the index to its " "final destination." msgstr "" "Bis jetzt haben wir Git's berühmten 'Index' gemieden, aber nun müssen wir " "uns mit ihm auseinandersetzen um das bisherige zu erklären. Der Index ist " "ein temporärer Bereitstellungsraum. Git tauscht selten Daten direkt zwischen " "Deinem Projekt und seiner Versionsgeschichte aus. Vielmehr schreibt Git die " "Daten zuerst in den Index, danach kopiert es die Daten aus dem Index an " "ihren eigentlichen Bestimmungsort." #. type: Plain text #: ../en/grandmaster.txt:77 msgid "" "For example, *commit -a* is really a two-step process. The first step places " "a snapshot of the current state of every tracked file into the index. The " "second step permanently records the snapshot now in the index. Committing " "without the *-a* option only performs the second step, and only makes sense " "after running commands that somehow change the index, such as *git add*." msgstr "" "Zum Beispiel ist *commit -a* eigentlich ein zweistufiger Prozess. Der erste " "Schritt erstellt einen Schnappschuß des aktuellen Status jeder überwachten " "Datei im Index. Der zweite Schritt speichert dauerhaft den Schnappschuß, der " "sich nun im Index befindet. Ein 'Commit' ohne die *-a* Option führt nur den " "zweiten Schritt aus und macht nur wirklich Sinn, wenn zuvor eine Anweisung " "angewendet wurde, welche den Index verändert, wie zum Beispiel *git add*." #. type: Plain text #: ../en/grandmaster.txt:79 msgid "" "Usually we can ignore the index and pretend we are reading straight from and " "writing straight to the history. On this occasion, we want finer control, so " "we manipulate the index. We place a snapshot of some, but not all, of our " "changes into the index, and then permanently record this carefully rigged " "snapshot." msgstr "" "Normalerweise können wir den Index ignorieren und so tun als würden wir " "direkt aus der Versionsgeschichte lesen oder in sie schreiben. In diesem " "Fall wollen wir aber mehr Kontrolle, also manipulieren wir den Index. Wir " "erstellen einen Schnappschuß einiger, aber nicht aller unser Änderungen im " "Index und speichern dann diesen sorgfältig zusammengestellten Schnappschuß " "permanent." #. type: Plain text #: ../en/grandmaster.txt:81 msgid "=== Don't Lose Your HEAD ===" msgstr "=== Verliere nicht Deinen KOPF ===" #. type: Plain text #: ../en/grandmaster.txt:83 msgid "" "The HEAD tag is like a cursor that normally points at the latest commit, " "advancing with each new commit. Some Git commands let you move it. For " "example:" msgstr "" "Der HEAD Bezeichner ist wie ein Cursor, der normalerweise auf den jüngsten " "'Commit' zeigt und mit jedem neuen 'Commit' voranschreitet. Einige Git " "Anweisungen lassen Dich ihn manipulieren. Zum Beispiel:" #. type: Plain text #: ../en/grandmaster.txt:85 #, no-wrap msgid " $ git reset HEAD~3\n" msgstr " $ git reset HEAD~3\n" #. type: Plain text #: ../en/grandmaster.txt:87 msgid "" "will move the HEAD three commits back. Thus all Git commands now act as if " "you hadn't made those last three commits, while your files remain in the " "present. See the help page for some applications." msgstr "" "bewegt den HEAD Bezeichner drei 'Commits' zurück. Dadurch agieren nun alle " "Git Anweisungen als hätte es die drei letzten 'Commits' nicht gegeben, " "während deine Dateien unverändert erhalten bleiben. Siehe auf der Git " "Hilfeseite für einige Anwendungsbeispiele." #. type: Plain text #: ../en/grandmaster.txt:89 msgid "" "But how can you go back to the future? The past commits know nothing of the " "future." msgstr "" "Aber wie kannst Du zurück in die Zukunft? Die vergangenen 'Commits' wissen " "nichts von der Zukunft." #. type: Plain text #: ../en/grandmaster.txt:91 msgid "If you have the SHA1 of the original HEAD then:" msgstr "Wenn Du den SHA1 Schlüssel vom originalen HEAD hast, dann:" #. type: Plain text #: ../en/grandmaster.txt:93 #, no-wrap msgid " $ git reset 1b6d\n" msgstr " $ git reset 1b6d\n" #. type: Plain text #: ../en/grandmaster.txt:95 msgid "" "But suppose you never took it down? Don't worry: for commands like these, " "Git saves the original HEAD as a tag called ORIG_HEAD, and you can return " "safe and sound with:" msgstr "" "Aber stell Dir vor, Du hast ihn niemals notiert? Keine Sorge: Für solche " "Anweisungen sichert Git den original HEAD als Bezeichner mit dem Namen " "ORIG_HEAD und Du kannst gesund und munter zurückkehren mit:" #. type: Plain text #: ../en/grandmaster.txt:97 #, no-wrap msgid " $ git reset ORIG_HEAD\n" msgstr " $ git reset ORIG_HEAD\n" #. type: Plain text #: ../en/grandmaster.txt:99 msgid "=== HEAD-hunting ===" msgstr "=== KOPF-Jagd ===" #. type: Plain text #: ../en/grandmaster.txt:101 msgid "" "Perhaps ORIG_HEAD isn't enough. Perhaps you've just realized you made a " "monumental mistake and you need to go back to an ancient commit in a long-" "forgotten branch." msgstr "" "Möglicherweise reicht ORIG_HEAD nicht aus. Vielleicht hast Du gerade " "bemerkt, dass Du einen kapitalen Fehler gemacht hast und nun musst Du zu " "einem uralten 'Commit' in einem länst vergessenen 'Branch' zurück." #. type: Plain text #: ../en/grandmaster.txt:106 msgid "" "By default, Git keeps a commit for at least two weeks, even if you ordered " "Git to destroy the branch containing it. The trouble is finding the " "appropriate hash. You could look at all the hash values in `.git/objects` " "and use trial and error to find the one you want. But there's a much easier " "way." msgstr "" "Standardmäßig behält Git einen 'Commit' für mindesten zwei Wochen, sogar " "wenn Du Git anweist den 'Branch' zu zerstören, in dem er enthalten ist. Das " "Problem ist, den entsprechenden SHA1-Wert zu finden. Du kannst Dir alle SHA1-" "Werte in `.git/objects` vornehmen und ausprobieren ob Du den gesuchten " "'Commit' findest. Aber es gibt einen viel einfacheren Weg." #. type: Plain text #: ../en/grandmaster.txt:108 msgid "" "Git records every hash of a commit it computes in `.git/logs`. The " "subdirectory `refs` contains the history of all activity on all branches, " "while the file `HEAD` shows every hash value it has ever taken. The latter " "can be used to find hashes of commits on branches that have been " "accidentally lopped off." msgstr "" "Git speichert jeden errechneten SHA1-Wert eines 'Commits' in `.git/logs`. " "Das Unterverzeichnis `refs` enthält den Verlauf aller Aktivitäten auf allen " "'Branches', während `HEAD` alle SHA1-Werte enthält, die jemals diese " "Bezeichnung hatten. Die letztere kann verwendet werden um SHA1-Werte von " "'Commits' zu finden, die sich in einem 'Branch' befanden, der versehentlich " "gestutzt wurde." #. type: Plain text #: ../en/grandmaster.txt:110 msgid "" "The reflog command provides a friendly interface to these log files. Try" msgstr "" "Die reflog Anweisung bietet eine benutzerfreundliche Schnittstelle zu diesen " "Logdateien. Versuche" #. type: Plain text #: ../en/grandmaster.txt:112 #, no-wrap msgid " $ git reflog\n" msgstr " $ git reflog\n" #. type: Plain text #: ../en/grandmaster.txt:114 msgid "Instead of cutting and pasting hashes from the reflog, try:" msgstr "" "Anstatt SHA1-Werte aus dem reflog zu kopieren und einzufügen, versuche:" #. type: Plain text #: ../en/grandmaster.txt:116 #, no-wrap msgid " $ git checkout \"@{10 minutes ago}\"\n" msgstr " $ git checkout \"@{10 minutes ago}\"\n" #. type: Plain text #: ../en/grandmaster.txt:118 msgid "Or checkout the 5th-last visited commit via:" msgstr "Oder rufe den fünftletzten 'Commit' ab, mit:" #. type: Plain text #: ../en/grandmaster.txt:120 #, no-wrap msgid " $ git checkout \"@{5}\"\n" msgstr " $ git checkout \"@{5}\"\n" #. type: Plain text #: ../en/grandmaster.txt:122 msgid "" "See the ``Specifying Revisions'' section of *git help rev-parse* for more." msgstr "" "Siehe in der ``Specifying Revisions'' Sektion von *git help rev-parse* für " "mehr." #. type: Plain text #: ../en/grandmaster.txt:125 msgid "" "You may wish to configure a longer grace period for doomed commits. For " "example:" msgstr "" "Vielleicht möchtest Du eine längere Gnadenfrist für todgeweihte 'Commits' " "konfigurieren. Zum Beispiel:" #. type: Plain text #: ../en/grandmaster.txt:127 #, no-wrap msgid " $ git config gc.pruneexpire \"30 days\"\n" msgstr " $ git config gc.pruneexpire \"30 days\"\n" #. type: Plain text #: ../en/grandmaster.txt:130 msgid "" "means a deleted commit will only be permanently lost once 30 days have " "passed and *git gc* is run." msgstr "" "bedeutet, ein gelöschter 'Commit' wird nur dann endgültig verloren sein, " "nachdem 30 Tage vergangen sind und *git gc* ausgeführt wurde." #. type: Plain text #: ../en/grandmaster.txt:132 msgid "You may also wish to disable automatic invocations of *git gc*:" msgstr "" "Du magst vielleicht auch das automatische Ausführen von *git gc* abstellen:" #. type: Plain text #: ../en/grandmaster.txt:134 #, no-wrap msgid " $ git config gc.auto 0\n" msgstr " $ git config gc.auto 0\n" #. type: Plain text #: ../en/grandmaster.txt:136 msgid "" "in which case commits will only be deleted when you run *git gc* manually." msgstr "" "wodurch 'Commits' nur noch gelöscht werden, wenn Du *git gc* manuell " "aufrufst." #. type: Plain text #: ../en/grandmaster.txt:138 msgid "=== Building On Git ===" msgstr "=== Auf Git bauen ===" #. type: Plain text #: ../en/grandmaster.txt:140 msgid "" "In true UNIX fashion, Git's design allows it to be easily used as a low-" "level component of other programs, such as GUI and web interfaces, " "alternative command-line interfaces, patch managements tools, importing and " "conversion tools and so on. In fact, some Git commands are themselves " "scripts standing on the shoulders of giants. With a little tinkering, you " "can customize Git to suit your preferences." msgstr "" "In echter UNIX Sitte erlaubt es Git's Design, dass es auf einfache Weise als " "Low-Level-Komponente von anderen Programmen benutzt werden kann, wie zum " "Beispiel grafischen Benutzeroberflächen und Internetanwendungen, alternative " "Kommandozeilenanwendungen, Patch-Werkzeugen, Import- und " "Konvertierungswerkzeugen und so weiter. Sogar einige Git Anweisungen selbst " "sind nur winzige Skripte, wie Zwerge auf den Schultern von Riesen. Mit ein " "bisschen Handarbeit kannst Du Git anpassen, damit es Deinen Anforderungen " "entspricht." #. type: Plain text #: ../en/grandmaster.txt:143 msgid "" "One easy trick is to use built-in Git aliases to shorten your most " "frequently used commands:" msgstr "" "Ein einfacher Trick ist es die in Git integrierte Aliasfunktion zu verwenden " "um die am häufigsten benutzten Anweisungen zu verkürzen:" #. type: Plain text #: ../en/grandmaster.txt:148 #, no-wrap msgid "" " $ git config --global alias.co checkout\n" " $ git config --global --get-regexp alias # display current aliases\n" " alias.co checkout\n" " $ git co foo # same as 'git checkout foo'\n" msgstr "" " $ git config --global alias.co checkout\n" " $ git config --global --get-regexp alias # display current aliases\n" " alias.co checkout\n" " $ git co foo # same as 'git checkout foo'\n" #. type: Plain text #: ../en/grandmaster.txt:151 msgid "" "Another is to print the current branch in the prompt, or window title. " "Invoking" msgstr "" "Etwas anderes ist der aktuelle 'Branch' im Prompt oder Fenstertitel. Die " "Anweisung" #. type: Plain text #: ../en/grandmaster.txt:153 #, no-wrap msgid " $ git symbolic-ref HEAD\n" msgstr " $ git symbolic-ref HEAD\n" #. type: Plain text #: ../en/grandmaster.txt:156 msgid "" "shows the current branch name. In practice, you most likely want to remove " "the \"refs/heads/\" and ignore errors:" msgstr "" "zeigt den Namen des aktuellen 'Branch'. In der Praxis möchtest Du aber das " "\"refs/heads/\" entfernen und Fehler ignorieren:" #. type: Plain text #: ../en/grandmaster.txt:158 #, no-wrap msgid " $ git symbolic-ref HEAD 2> /dev/null | cut -b 12-\n" msgstr " $ git symbolic-ref HEAD 2> /dev/null | cut -b 12-\n" #. type: Plain text #: ../en/grandmaster.txt:162 msgid "" "The +contrib+ subdirectory is a treasure trove of tools built on Git. In " "time, some of them may be promoted to official commands. On Debian and " "Ubuntu, this directory lives at +/usr/share/doc/git-core/contrib+." msgstr "" "Das +contrib+ Unterverzeichnis ist eine Fundgrube von Werkzeugen, die auf " "Git aufbauen. Mit der Zeit können einige davon zu offiziellen Anweisungen " "befördert werden. Auf Debian und Ubuntu, findet man dieses Verzeichnis unter " "+/usr/share/doc/git-core/contrib+." #. type: Plain text #: ../en/grandmaster.txt:164 msgid "" "One popular resident is +workdir/git-new-workdir+. Via clever symlinking, " "this script creates a new working directory whose history is shared with the " "original repository:" msgstr "" "Ein beliebter Vertreter ist +workdir/git-new-workdir+. Durch cleveres " "verlinken erzeugt dieses Skript ein neues Arbeitsverzeichis, das seine " "Versionsgeschichte mit dem original 'Repository' teilt:" #. type: Plain text #: ../en/grandmaster.txt:166 #, no-wrap msgid " $ git-new-workdir an/existing/repo new/directory\n" msgstr " $ git-new-workdir ein/existierendes/repo neues/verzeichnis\n" #. type: Plain text #: ../en/grandmaster.txt:168 msgid "" "The new directory and the files within can be thought of as a clone, except " "since the history is shared, the two trees automatically stay in sync. " "There's no need to merge, push, or pull." msgstr "" "Das neue Verzeichnis und die Dateien darin kann man sich als 'Clone' " "vorstellen, mit dem Unterschied, dass durch die gemeinschaftliche " "Versionsgeschichte die beiden Versionen automatisch synchron bleiben. Eine " "Synchronisierung mittels 'merge', 'push' oder 'pull' ist nicht notwendig." #. type: Plain text #: ../en/grandmaster.txt:170 msgid "=== Daring Stunts ===" msgstr "=== Gewagte Kunststücke ===" #. type: Plain text #: ../en/grandmaster.txt:174 msgid "" "These days, Git makes it difficult for the user to accidentally destroy " "data. But if you know what you are doing, you can override safeguards for " "common commands." msgstr "" "Heutzutage macht es Git dem Anwender schwer versehentlich Daten zu " "zerstören. Aber, wenn man weiß was man tut, kann man die Schutzmaßnahmen der " "häufigsten Anweisungen umgehen." #. type: Plain text #: ../en/grandmaster.txt:176 #, no-wrap msgid "*Checkout*: Uncommitted changes cause checkout to fail. To destroy your changes, and checkout a given commit anyway, use the force flag:\n" msgstr "*Checkout*: Nicht versionierte Änderungen lassen 'checkout' scheitern. Um trotzdem die Änderungen zu zerstören und einen vorhandenen 'Commit' abzurufen, benutzen wir die 'force' Option:\n" #. type: Plain text #: ../en/grandmaster.txt:178 #, no-wrap msgid " $ git checkout -f HEAD^\n" msgstr " $ git checkout -f HEAD^\n" #. type: Plain text #: ../en/grandmaster.txt:180 msgid "" "On the other hand, if you specify particular paths for checkout, then there " "are no safety checks. The supplied paths are quietly overwritten. Take care " "if you use checkout in this manner." msgstr "" "Auf der anderen Seite, wenn Du einen speziellen Pfad für 'checkout' angibst, " "gibt es keinen Sicherheitsüberprüfungen mehr. Der angegebene Pfad wird " "stillschweigend überschrieben. Sei vorsichtig, wenn Du 'checkout' auf diese " "Weise benutzt." #. type: Plain text #: ../en/grandmaster.txt:182 #, no-wrap msgid "*Reset*: Reset also fails in the presence of uncommitted changes. To force it through, run:\n" msgstr "*Reset*: Reset versagt auch, wenn unversionierte Änderungen vorliegen. Um es zu erzwingen, verwende:\n" #. type: Plain text #: ../en/grandmaster.txt:184 #, no-wrap msgid " $ git reset --hard 1b6d\n" msgstr " $ git reset --hard 1b6d\n" #. type: Plain text #: ../en/grandmaster.txt:186 #, no-wrap msgid "*Branch*: Deleting branches fails if this causes changes to be lost. To force a deletion, type:\n" msgstr "*Branch*: 'Branches' zu löschen scheitert ebenfalls, wenn dadurch Änderungen verloren gehen. Um das Löschen zu erzwingen, gib ein:\n" #. type: Plain text #: ../en/grandmaster.txt:188 #, no-wrap msgid " $ git branch -D dead_branch # instead of -d\n" msgstr " $ git branch -D dead_branch # instead of -d\n" #. type: Plain text #: ../en/grandmaster.txt:190 msgid "" "Similarly, attempting to overwrite a branch via a move fails if data loss " "would ensue. To force a branch move, type:" msgstr "" "Ebenso scheitert der Versuch einen 'Branch' durch ein 'move' zu " "überschreiben, wenn das einen Datenverlust zur Folge hat. Um das Verschieben " "zu erzwingen, gib ein:" #. type: Plain text #: ../en/grandmaster.txt:192 #, no-wrap msgid " $ git branch -M source target # instead of -m\n" msgstr " $ git branch -M source target # instead of -m\n" #. type: Plain text #: ../en/grandmaster.txt:197 msgid "" "Unlike checkout and reset, these two commands defer data destruction. The " "changes are still stored in the .git subdirectory, and can be retrieved by " "recovering the appropriate hash from `.git/logs` (see \"HEAD-hunting\" " "above). By default, they will be kept for at least two weeks." msgstr "" "Anders als bei 'checkout' und 'reset' verschieben diese beiden Anweisungen " "das Zerstören der Daten. Die Änderungen bleiben im .git Unterverzeichnis " "gespeichert und können wieder hergestellt werden, wenn der entsprechende " "SHA1-Wert aus `.git/logs` ermittelt wird (siehe \"KOPF-Jagd\" oben). " "Standardmäßig bleiben die Daten mindestens zwei Wochen erhalten." #. type: Plain text #: ../en/grandmaster.txt:201 #, no-wrap msgid "" "*Clean*: Some git commands refuse to proceed because they're worried about\n" "clobbering untracked files. If you're certain that all untracked files and\n" "directories are expendable, then delete them mercilessly with:\n" msgstr "*Clean*: Verschiedene git Anweisungen scheitern, weil sie Konflikte mit unversionierten Dateien vermuten. Wenn Du sicher bist, dass alle unversionierten Dateien und Verzeichnisse entbehrlich sind, dann lösche diese gnadenlos mit:\n" #. type: Plain text #: ../en/grandmaster.txt:203 #, no-wrap msgid " $ git clean -f -d\n" msgstr " $ git clean -f -d\n" #. type: Plain text #: ../en/grandmaster.txt:205 msgid "Next time, that pesky command will work!" msgstr "Beim nächsten Mal werden diese lästigen Anweisung gehorchen!" #. type: Plain text #: ../en/grandmaster.txt:207 msgid "=== Preventing Bad Commits ===" msgstr "=== Verhindere schlechte 'Commits' ===" #. type: Plain text #: ../en/grandmaster.txt:212 msgid "" "Stupid mistakes pollute my repositories. Most frightening are missing files " "due to a forgotten *git add*. Lesser transgressions are trailing whitespace " "and unresolved merge conflicts: though harmless, I wish these never appeared " "on the public record." msgstr "" "Dumme Fehler verschmutzen meine 'Repositories'. Am schrecklichsten sind " "fehlende Dateien wegen eines vergessenen *git add*. Kleinere Verfehlungen " "sind Leerzeichen am Zeilenende und ungelöste 'merge'-Konflikte: obwohl sie " "harmlos sind, wünschte ich, sie würden nie in der Öffentlichkeit erscheinen." #. type: Plain text #: ../en/grandmaster.txt:214 msgid "" "If only I had bought idiot insurance by using a _hook_ to alert me about " "these problems:" msgstr "" "Wenn ich doch nur eine Trottelversicherung abgeschlossen hätte, durch " "Verwendung eines _hook_, der mich bei solchen Problemen alarmiert." #. type: Plain text #: ../en/grandmaster.txt:217 #, no-wrap msgid "" " $ cd .git/hooks\n" " $ cp pre-commit.sample pre-commit # Older Git versions: chmod +x pre-commit\n" msgstr "" " $ cd .git/hooks\n" " $ cp pre-commit.sample pre-commit # Older Git versions: chmod +x pre-commit\n" #. type: Plain text #: ../en/grandmaster.txt:220 msgid "" "Now Git aborts a commit if useless whitespace or unresolved merge conflicts " "are detected." msgstr "" "Nun bricht Git einen 'Commit' ab, wenn es überflüssige Leerzeichen am " "Zeilenende oder ungelöste 'merge'-Konflikte entdeckt." #. type: Plain text #: ../en/grandmaster.txt:223 msgid "" "For this guide, I eventually added the following to the beginning of the " "*pre-commit* hook to guard against absent-mindedness:" msgstr "" "Für diese Anleitung hätte ich vielleicht am Anfang des *pre-commit* 'hook' " "folgendes hinzugefügt, zum Schutz vor Zerstreutheit:" #. type: Plain text #: ../en/grandmaster.txt:228 #, no-wrap msgid "" " if git ls-files -o | grep '\\.txt$'; then\n" " echo FAIL! Untracked .txt files.\n" " exit 1\n" " fi\n" msgstr "" " if git ls-files -o | grep '\\.txt$'; then\n" " echo FAIL! Untracked .txt files.\n" " exit 1\n" " fi\n" #. type: Plain text #: ../en/grandmaster.txt:232 msgid "" "Several git operations support hooks; see *git help hooks*. We activated the " "sample *post-update* hook earlier when discussing Git over HTTP. This runs " "whenever the head moves. The sample post-update script updates files Git " "needs for communication over Git-agnostic transports such as HTTP." msgstr "" "Viele Git Operationen unterstützen 'hooks'; siehe *git help hooks*. Wir " "haben den Beispiel 'hook' *post-update* aktiviert, weiter oben im Abschnitt " "Git über HTTP. Dieser läuft immer, wenn der 'HEAD' sich bewegt. Das Beispiel " "'post-update' Skript aktualisiert Dateien, welche Git für die Kommunikation " "über 'Git-agnostic transports' wie z.B. HTTP benötigt." gitmagic-20160304/de/pot/secrets.po0000644000175000017500000007577012666307504016222 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010, 2011. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: bennlynn@gmail.com\n" "POT-Creation-Date: 2010-10-30 08:21+0300\n" "PO-Revision-Date: 2011-07-07 18:53+0200\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/secrets.txt:2 msgid "== Secrets Revealed ==" msgstr "== Aufgedeckte Geheimnisse ==" #. type: Plain text #: ../en/secrets.txt:4 msgid "" "We take a peek under the hood and explain how Git performs its miracles. I " "will skimp over details. For in-depth descriptions refer to http://www." "kernel.org/pub/software/scm/git/docs/user-manual.html[the user manual]." msgstr "" "Wir werfen einen Blick unter die Motorhaube und erklären, wie Git seine " "Wunder vollbringt. Ich werde nicht ins Detail gehen. Für tiefer gehende " "Erklärungen verweise ich auf das http://www.kernel.org/pub/software/scm/git/" "docs/user-manual.html[englischsprachige Benutzerhandbuch]." #. type: Plain text #: ../en/secrets.txt:6 msgid "=== Invisibility ===" msgstr "=== Unsichtbarkeit ===" #. type: Plain text #: ../en/secrets.txt:8 msgid "" "How can Git be so unobtrusive? Aside from occasional commits and merges, you " "can work as if you were unaware that version control exists. That is, until " "you need it, and that's when you're glad Git was watching over you the whole " "time." msgstr "" "Wie kann Git so unauffällig sein? Abgesehen von gelegentlichen 'Commits' und " "'Merges' kannst Du arbeiten, als würde die Versionsverwaltung nicht " "existieren. Das heißt, bis Du sie brauchst. Und das ist, wenn Du froh bist, " "dass Git die ganze Zeit über Dich gewacht hat." #. type: Plain text #: ../en/secrets.txt:10 msgid "" "Other version control systems force you to constantly struggle with red tape " "and bureaucracy. Permissions of files may be read-only unless you explicitly " "tell a central server which files you intend to edit. The most basic " "commands may slow to a crawl as the number of users increases. Work grinds " "to a halt when the network or the central server goes down." msgstr "" "Andere Versionsverwaltungssysteme zwingen Dich ständig Dich mit " "Verwaltungskram und Bürokratie herumzuschlagen. Dateien sind können " "schreibgeschützt sein, bis Du einem zentralen Server mitteilst, welche " "Dateien Du gerne bearbeiten möchtest. Die einfachsten Befehle werden bis zum " "Schneckentempo verlangsamt, wenn die Anzahl der Anwender steigt. Deine " "Arbeit kommt zum Stillstand, wenn das Netzwerk oder der zentrale Server weg " "sind." #. type: Plain text #: ../en/secrets.txt:12 msgid "" "In contrast, Git simply keeps the history of your project in the `.git` " "directory in your working directory. This is your own copy of the history, " "so you can stay offline until you want to communicate with others. You have " "total control over the fate of your files because Git can easily recreate a " "saved state from `.git` at any time." msgstr "" "Im Gegensatz dazu hält Git seinen Verlauf einfach im `.git` Verzeichnis von " "Deinem Arbeitsverzeichnis. Das ist Deine eigene Kopie der " "Versionsgeschichte, damit kannst Du so lange offline bleiben, bis Du mit " "anderen kommunizieren willst. Du hast die absolute Kontrolle über das " "Schicksal Deiner Dateien, denn Git kann jederzeit einfach einen gesicherten " "Stand aus `.git` wiederherstellen." #. type: Plain text #: ../en/secrets.txt:14 msgid "=== Integrity ===" msgstr "=== Integrität ===" #. type: Plain text #: ../en/secrets.txt:16 msgid "" "Most people associate cryptography with keeping information secret, but " "another equally important goal is keeping information safe. Proper use of " "cryptographic hash functions can prevent accidental or malicious data " "corruption." msgstr "" "Die meisten Leute verbinden mit Kryptographie die Geheimhaltung von " "Informationen, aber ein genau so wichtiges Ziel ist es Informationen zu " "sichern. Die richtige Anwendung von kryptographischen Hash-Funktionen kann " "einen versehentlichen oder bösartigen Datenverlust verhindern." #. type: Plain text #: ../en/secrets.txt:18 msgid "" "A SHA1 hash can be thought of as a unique 160-bit ID number for every string " "of bytes you'll encounter in your life. Actually more than that: every " "string of bytes that any human will ever use over many lifetimes." msgstr "" "Einen SHA1-Hash-Wert kann man sich als eindeutige 160-Bit Identitätsnummer " "für jegliche Zeichenkette vorstellen, welche Dir in Deinem ganzen Leben " "begegnen wird. Sogar mehr als das: jegliche Zeichenfolge, die alle Menschen " "über mehrere Generationen verwenden." #. type: Plain text #: ../en/secrets.txt:20 msgid "" "As a SHA1 hash is itself a string of bytes, we can hash strings of bytes " "containing other hashes. This simple observation is surprisingly useful: " "look up 'hash chains'. We'll later see how Git uses it to efficiently " "guarantee data integrity." msgstr "" "Ein SHA1-Hash-Wert selbst ist eine Zeichenfolge von Bytes. Wir können SHA1-" "Hash-Werte aus Zeichenfolgen generieren, die selbst SHA1-Hash-Werte " "enthalten. Diese einfache Beobachtung ist überraschend nützlich: suche nach " "'hash chains'. Wir werden später sehen, wie Git diese nutzt um effizient die " "Datenintegrität zu garantieren." #. type: Plain text #: ../en/secrets.txt:22 msgid "" "Briefly, Git keeps your data in the `.git/objects` subdirectory, where " "instead of normal filenames, you'll find only IDs. By using IDs as " "filenames, as well as a few lockfiles and timestamping tricks, Git " "transforms any humble filesystem into an efficient and robust database." msgstr "" "Kurz gesagt, Git hält Deine Daten in dem `.git/objects` Unterverzeichnis, wo " "Du anstelle von normalen Dateinamen nur Identitätsnummern findest. Durch die " "Verwendung von Identitätsnummern als Dateiname, zusammen mit ein paar " "Sperrdateien und Zeitstempeltricks, macht Git aus einem einfachen " "Dateisystem eine effiziente und robuste Datenbank." #. type: Plain text #: ../en/secrets.txt:24 msgid "=== Intelligence ===" msgstr "=== Intelligenz ===" #. type: Plain text #: ../en/secrets.txt:26 msgid "" "How does Git know you renamed a file, even though you never mentioned the " "fact explicitly? Sure, you may have run *git mv*, but that is exactly the " "same as a *git rm* followed by a *git add*." msgstr "" "Woher weiß Git, dass Du eine Datei umbenannt hast, obwohl Du es ihm niemals " "explizit mitgeteilt hast? Sicher, Du hast vielleicht *git mv* benutzt, aber " "das ist exakt das selbe wie *git rm* gefolgt von *git add*." #. type: Plain text #: ../en/secrets.txt:28 msgid "" "Git heuristically ferrets out renames and copies between successive " "versions. In fact, it can detect chunks of code being moved or copied around " "between files! Though it cannot cover all cases, it does a decent job, and " "this feature is always improving. If it fails to work for you, try options " "enabling more expensive copy detection, and consider upgrading." msgstr "" "Git stöbert Umbenennungen und Kopien zwischen aufeinander folgenden " "Versionen heuristisch auf. Vielmehr kann es sogar Codeblöcke erkennen, die " "zwischen Dateien hin und her kopiert oder verschoben wurden! Jedoch kann es " "nicht alle Fälle abdecken, aber es leistet ordentliche Arbeit und diese " "Eigenschaft wird immer besser. Wenn es bei Dir nicht funktioniert, versuche " "Optionen zur aufwendigeren Erkennung von Kopien oder erwäge einen Upgrade." #. type: Plain text #: ../en/secrets.txt:30 msgid "=== Indexing ===" msgstr "=== Indizierung ===" #. type: Plain text #: ../en/secrets.txt:32 msgid "" "For every tracked file, Git records information such as its size, creation " "time and last modification time in a file known as the 'index'. To determine " "whether a file has changed, Git compares its current stats with those cached " "in the index. If they match, then Git can skip reading the file again." msgstr "" "Für jede überwachte Datei speichert Git Informationen wie deren Größe, ihren " "Erstellzeitpunkt und den Zeitpunkt der letzten Bearbeitung in einer Datei " "die wir als 'Index' kennen. Um zu ermitteln, ob eine Datei verändert wurde, " "vergleicht Git den aktuellen Status mit dem im Index gespeicherten. Stimmen " "diese Daten überein, kann Git das Lesen des Dateiinhalts überspringen." #. type: Plain text #: ../en/secrets.txt:35 msgid "" "Since stat calls are considerably faster than file reads, if you only edit a " "few files, Git can update its state in almost no time." msgstr "" "Da das Abfragen des Dateistatus erheblich schneller ist als das Lesen der " "Datei, kann Git, wenn Du nur ein paar Dateien verändert hast, seinen Status " "im Nu aktualisieren." #. type: Plain text #: ../en/secrets.txt:40 msgid "" "We stated earlier that the index is a staging area. Why is a bunch of file " "stats a staging area? Because the add command puts files into Git's database " "and updates these stats, while the commit command, without options, creates " "a commit based only on these stats and the files already in the database." msgstr "" "Wir haben früher festgestellt, dass der Index ein Bereitstellungsraum ist. " "Warum kann ein Haufen von Dateistatusinformationen ein Bereitstellungsraum " "sein? Weil die 'add' Anweisung Dateien in die Git Datenbank befördert und " "die Dateistatusinformationen aktualisiert, während die 'commit' Anweisung, " "ohne Optionen, einen 'Commit' nur auf Basis der Dateistatusinformationen " "erzeugt, weil die Dateien ja schon in der Datenbank sind." #. type: Plain text #: ../en/secrets.txt:42 msgid "=== Git's Origins ===" msgstr "=== Git's Wurzeln ===" #. type: Plain text #: ../en/secrets.txt:44 msgid "" "This http://lkml.org/lkml/2005/4/6/121[Linux Kernel Mailing List post] " "describes the chain of events that led to Git. The entire thread is a " "fascinating archaeological site for Git historians." msgstr "" "Dieser http://lkml.org/lkml/2005/4/6/121['Linux Kernel Mailing List' " "Beitrag] beschreibt die Kette von Ereignissen, die zu Git geführt haben. Der " "ganze Beitrag ist eine faszinierende archäologische Seite für Git Historiker." #. type: Plain text #: ../en/secrets.txt:46 msgid "=== The Object Database ===" msgstr "=== Die Objektdatenbank ===" #. type: Plain text #: ../en/secrets.txt:52 msgid "" "Every version of your data is kept in the 'object database', which lives in " "the subdirectory `.git/objects`; the other residents of `.git/` hold lesser " "data: the index, branch names, tags, configuration options, logs, the " "current location of the head commit, and so on. The object database is " "elementary yet elegant, and the source of Git's power." msgstr "" "Jegliche Version Deiner Daten wird in der Objektdatenbank gehalten, welche " "im Unterverzeichnis `.git/objects` liegt; Die anderen Orte in `.git/` " "enthalten weniger wichtige Daten: den Index, 'Branch' Namen, Bezeichner " "('tags'), Konfigurationsoptionen, Logdateien, die Position des aktuellen " "'HEAD Commit' und so weiter. Die Objektdatenbank ist einfach aber trotzdem " "elegant und sie ist die Quelle von Git's Macht." #. type: Plain text #: ../en/secrets.txt:55 msgid "" "Each file within `.git/objects` is an 'object'. There are 3 kinds of objects " "that concern us: 'blob' objects, 'tree' objects, and 'commit' objects." msgstr "" "Jede Datei in `.git/objects` ist ein 'Objekt'. Es gibt drei Arten von " "Objekten die uns betreffen: 'Blob'-, 'Tree'-, und 'Commit'-Objekte." #. type: Plain text #: ../en/secrets.txt:57 msgid "=== Blobs ===" msgstr "=== Blobs ===" #. type: Plain text #: ../en/secrets.txt:59 msgid "" "First, a magic trick. Pick a filename, any filename. In an empty directory:" msgstr "" "Zuerst ein Zaubertrick. Suche Dir einen Dateinamen aus, irgendeinen. In " "einem leeren Verzeichnis:" #. type: Plain text #: ../en/secrets.txt:64 #, no-wrap msgid "" " $ echo sweet > YOUR_FILENAME\n" " $ git init\n" " $ git add .\n" " $ find .git/objects -type f\n" msgstr "" " $ echo sweet > DEIN_DATEINAME\n" " $ git init\n" " $ git add .\n" " $ find .git/objects -type f\n" #. type: Plain text #: ../en/secrets.txt:66 msgid "You'll see +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+." msgstr "" "Du wirst folgendes sehen: +.git/objects/" "aa/823728ea7d592acc69b36875a482cdf3fd5c8d+." #. type: Plain text #: ../en/secrets.txt:69 msgid "" "How do I know this without knowing the filename? It's because the SHA1 hash " "of:" msgstr "" "Wie konnte ich das wissen, ohne den Dateiname zu kennen? Weil der SHA1-Hash-" "Wert von:" #. type: Plain text #: ../en/secrets.txt:71 #, no-wrap msgid " \"blob\" SP \"6\" NUL \"sweet\" LF\n" msgstr " \"blob\" SP \"6\" NUL \"sweet\" LF\n" #. type: Plain text #: ../en/secrets.txt:75 msgid "" "is aa823728ea7d592acc69b36875a482cdf3fd5c8d, where SP is a space, NUL is a " "zero byte and LF is a linefeed. You can verify this by typing:" msgstr "" "aa823728ea7d592acc69b36875a482cdf3fd5c8d ist. Wobei SP ein Leerzeichen ist, " "NUL ist ein Nullbyte und LF ist ein Zeilenumbruch. Das kannst Du " "kontrollieren, durch die Eingabe von:" #. type: Plain text #: ../en/secrets.txt:77 #, no-wrap msgid " $ printf \"blob 6\\000sweet\\n\" | sha1sum\n" msgstr " $ printf \"blob 6\\000sweet\\n\" | sha1sum\n" #. type: Plain text #: ../en/secrets.txt:84 msgid "" "Git is 'content-addressable': files are not stored according to their " "filename, but rather by the hash of the data they contain, in a file we call " "a 'blob object'. We can think of the hash as a unique ID for a file's " "contents, so in a sense we are addressing files by their content. The " "initial `blob 6` is merely a header consisting of the object type and its " "length in bytes; it simplifies internal bookkeeping." msgstr "" "Git ist 'assoziativ': Dateien werden nicht nach Ihren Namen gespeichert, " "sondern eher nach dem SHA1-Hash-Wert der Daten, welche sie enthalten, in " "einer Datei, die wir als 'Blob'-Objekt bezeichnen. Wir können uns den SHA1-" "Hash-Wert als eindeutige Identnummer des Dateiinhalts vorstellen, was " "sinngemäß bedeutet, dass die Dateien über ihren Inhalt adressiert werden. " "Das führende `blob 6` ist lediglich ein Vermerk, der sich aus dem Objekttyp " "und seiner Länge in Bytes zusammensetzt; er vereinfacht die interne " "Verwaltung." #. type: Plain text #: ../en/secrets.txt:87 msgid "" "Thus I could easily predict what you would see. The file's name is " "irrelevant: only the data inside is used to construct the blob object." msgstr "" "So konnte ich einfach vorhersagen, was Du sehen wirst. Der Dateiname ist " "irrelevant: nur der Dateiinhalt wird zum Erstellen des 'Blob'-Objekt " "verwendet." #. type: Plain text #: ../en/secrets.txt:91 msgid "" "You may be wondering what happens to identical files. Try adding copies of " "your file, with any filenames whatsoever. The contents of +.git/objects+ " "stay the same no matter how many you add. Git only stores the data once." msgstr "" "Du wirst Dich fragen, was mit identischen Dateien ist. Versuche Kopien " "Deiner Datei hinzuzufügen, mit beliebigen Dateinamen. Der Inhalt von +.git/" "objects+ bleibt der selbe, ganz egal wieviele Dateien Du hinzufügst. Git " "speichert den Dateiinhalt nur ein einziges Mal." #. type: Plain text #: ../en/secrets.txt:95 msgid "" "By the way, the files within +.git/objects+ are compressed with zlib so you " "should not stare at them directly. Filter them through http://www.zlib.net/" "zpipe.c[zpipe -d], or type:" msgstr "" "Übrigens, die Dateien in +.git/objects+ sind mit zlib komprimiert, Du " "solltest sie also nicht direkt anschauen. Filtere sie durch http://www.zlib." "net/zpipe.c[zpipe -d], oder gib ein:" #. type: Plain text #: ../en/secrets.txt:97 #, no-wrap msgid " $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d\n" msgstr " $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d\n" #. type: Plain text #: ../en/secrets.txt:99 msgid "which pretty-prints the given object." msgstr "was Dir das Objekt im Klartext anzeigt." #. type: Plain text #: ../en/secrets.txt:101 msgid "=== Trees ===" msgstr "=== 'Trees' ===" #. type: Plain text #: ../en/secrets.txt:104 msgid "" "But where are the filenames? They must be stored somewhere at some stage. " "Git gets around to the filenames during a commit:" msgstr "" "Aber wo sind die Dateinamen? Sie müssen irgendwo gespeichert sein. Git kommt " "beim 'Commit' dazu sich um die Dateinamen zu kümmern:" #. type: Plain text #: ../en/secrets.txt:107 #, no-wrap msgid "" " $ git commit # Type some message.\n" " $ find .git/objects -type f\n" msgstr "" " $ git commit # Schreibe eine Bemerkung.\n" " $ find .git/objects -type f\n" #. type: Plain text #: ../en/secrets.txt:109 msgid "" "You should now see 3 objects. This time I cannot tell you what the 2 new " "files are, as it partly depends on the filename you picked. We'll proceed " "assuming you chose ``rose''. If you didn't, you can rewrite history to make " "it look like you did:" msgstr "" "Du solltest nun drei Objekte sehen. Dieses mal kann ich Dir nicht sagen, wie " "die zwei neuen Dateien heißen, weil es zum Teil vom gewählten Dateiname " "abhängt, den Du ausgesucht hast. Fahren wir fort mit der Annahme, Du hast " "eine Datei ``rose'' genannt. Wenn nicht, kannst Du den Verlauf so " "umschreiben, dass es so aussieht als hättest Du es:" #. type: Plain text #: ../en/secrets.txt:112 #, no-wrap msgid "" " $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose'\n" " $ find .git/objects -type f\n" msgstr "" " $ git filter-branch --tree-filter 'mv DEIN_DATEINAME rose'\n" " $ find .git/objects -type f\n" #. type: Plain text #: ../en/secrets.txt:116 msgid "" "Now you should see the file +.git/objects/05/" "b217bb859794d08bb9e4f7f04cbda4b207fbe9+, because this is the SHA1 hash of " "its contents:" msgstr "" "Nun müsstest Du die Datei +.git/objects/05/" "b217bb859794d08bb9e4f7f04cbda4b207fbe9+ sehen, denn das ist der SHA1-Hash-" "Wert ihres Inhalts:" #. type: Plain text #: ../en/secrets.txt:118 #, no-wrap msgid " \"tree\" SP \"32\" NUL \"100644 rose\" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d\n" msgstr " \"tree\" SP \"32\" NUL \"100644 rose\" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d\n" #. type: Plain text #: ../en/secrets.txt:120 msgid "Check this file does indeed contain the above by typing:" msgstr "" "Prüfe, ob diese Datei tatsächlich dem obigen Inhalt entspricht, durch " "Eingabe von:" #. type: Plain text #: ../en/secrets.txt:122 #, no-wrap msgid " $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch\n" msgstr " $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch\n" #. type: Plain text #: ../en/secrets.txt:124 msgid "With zpipe, it's easy to verify the hash:" msgstr "Mit zpipe, ist es einfach den SHA1-Hash-Wert zu prüfen:" #. type: Plain text #: ../en/secrets.txt:126 #, no-wrap msgid " $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum\n" msgstr " $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum\n" #. type: Plain text #: ../en/secrets.txt:129 msgid "" "Hash verification is trickier via cat-file because its output contains more " "than the raw uncompressed object file." msgstr "" "Die SHA1-Hash-Wert Prüfung mit 'cat-file' ist etwas kniffliger, da dessen " "Ausgabe mehr als die rohe unkomprimierte Objektdatei enthält." #. type: Plain text #: ../en/secrets.txt:135 msgid "" "This file is a 'tree' object: a list of tuples consisting of a file type, a " "filename, and a hash. In our example, the file type is 100644, which means " "`rose` is a normal file, and the hash is the blob object that contains the " "contents of `rose'. Other possible file types are executables, symlinks or " "directories. In the last case, the hash points to a tree object." msgstr "" "Diese Datei ist ein 'Tree'-Objekt: eine Liste von Datensätzen, bestehend aus " "dem Dateityp, dem Dateinamen und einem SHA1-Hash-Wert. In unserem Beispiel " "ist der Dateityp 100644, was bedeutet, dass `rose` eine normale Datei ist " "und der SHA1-Hash-Wert entspricht dem 'Blob'-Objekt, welches den Inhalt von " "`rose` enthält. Andere mögliche Dateitypen sind ausführbare Programmdateien, " "symbolische Links oder Verzeichnisse. Im letzten Fall zeigt der SHA1-Hash-" "Wert auf ein 'Tree'-Objekt." #. type: Plain text #: ../en/secrets.txt:139 msgid "" "If you ran filter-branch, you'll have old objects you no longer need. " "Although they will be jettisoned automatically once the grace period " "expires, we'll delete them now to make our toy example easier to follow:" msgstr "" "Wenn Du 'filter-branch' aufrufst, bekommst Du alte Objekte, welche nicht " "länger benötigt werden. Obwohl sie automatisch über Bord geworfen werden, " "wenn ihre Gnadenfrist abgelaufen ist, wollen wir sie nun löschen, damit wir " "unserem Beispiel besser folgen können." #. type: Plain text #: ../en/secrets.txt:143 #, no-wrap msgid "" " $ rm -r .git/refs/original\n" " $ git reflog expire --expire=now --all\n" " $ git prune\n" msgstr "" " $ rm -r .git/refs/original\n" " $ git reflog expire --expire=now --all\n" " $ git prune\n" #. type: Plain text #: ../en/secrets.txt:150 msgid "" "For real projects you should typically avoid commands like this, as you are " "destroying backups. If you want a clean repository, it is usually best to " "make a fresh clone. Also, take care when directly manipulating +.git+: what " "if a Git command is running at the same time, or a sudden power outage " "occurs? In general, refs should be deleted with *git update-ref -d*, though " "usually it's safe to remove +refs/original+ by hand." msgstr "" "Für reale Projekte solltest Du solche Anweisungen üblicherweise vermeiden, " "da Du dadurch Datensicherungen zerstörst. Wenn Du ein sauberes 'Repository' " "willst, ist es am besten, einen neuen Klon anzulegen. Sei auch vorsichtig, " "wenn Du +.git+ direkt manipulierst: was, wenn zeitgleich ein Git Kommando " "ausgeführt wird oder plötzlich der Strom ausfällt? Generell sollten " "Referenzen mit *git update-ref -d* gelöscht werden, auch wenn es gewöhnlich " "sicher ist +refs/original+ von Hand zu löschen." #. type: Plain text #: ../en/secrets.txt:152 msgid "=== Commits ===" msgstr "=== 'Commits' ===" #. type: Plain text #: ../en/secrets.txt:156 msgid "" "We've explained 2 of the 3 objects. The third is a 'commit' object. Its " "contents depend on the commit message as well as the date and time it was " "created. To match what we have here, we'll have to tweak it a little:" msgstr "" "Wir haben nun zwei von drei Objekten erklärt. Das dritte ist ein 'Commit'-" "Objekt. Sein Inhalt hängt von der 'Commit'-Beschreibung ab, wie auch vom " "Zeitpunkt der Erstellung. Damit alles zu unserem Beispiel passt, müssen wir " "ein wenig tricksen:" #. type: Plain text #: ../en/secrets.txt:166 #, no-wrap msgid "" " $ git commit --amend -m Shakespeare # Change the commit message.\n" " $ git filter-branch --env-filter 'export\n" " GIT_AUTHOR_DATE=\"Fri 13 Feb 2009 15:31:30 -0800\"\n" " GIT_AUTHOR_NAME=\"Alice\"\n" " GIT_AUTHOR_EMAIL=\"alice@example.com\"\n" " GIT_COMMITTER_DATE=\"Fri, 13 Feb 2009 15:31:30 -0800\"\n" " GIT_COMMITTER_NAME=\"Bob\"\n" " GIT_COMMITTER_EMAIL=\"bob@example.com\"' # Rig timestamps and authors.\n" " $ find .git/objects -type f\n" msgstr "" " $ git commit --amend -m Shakespeare # Ändere die Bemerkung.\n" " $ git filter-branch --env-filter 'export\n" " GIT_AUTHOR_DATE=\"Fri 13 Feb 2009 15:31:30 -0800\"\n" " GIT_AUTHOR_NAME=\"Alice\"\n" " GIT_AUTHOR_EMAIL=\"alice@example.com\"\n" " GIT_COMMITTER_DATE=\"Fri, 13 Feb 2009 15:31:30 -0800\"\n" " GIT_COMMITTER_NAME=\"Bob\"\n" " GIT_COMMITTER_EMAIL=\"bob@example.com\"' # Manipuliere Zeitstempel und Autor.\n" " $ find .git/objects -type f\n" #. type: Plain text #: ../en/secrets.txt:170 msgid "" "You should now see +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ " "which is the SHA1 hash of its contents:" msgstr "" "Du solltest nun +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ " "finden, was dem SHA1-Hash-Wert seines Inhalts entspricht:" #. type: Plain text #: ../en/secrets.txt:177 #, no-wrap msgid "" " \"commit 158\" NUL\n" " \"tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9\" LF\n" " \"author Alice 1234567890 -0800\" LF\n" " \"committer Bob 1234567890 -0800\" LF\n" " LF\n" " \"Shakespeare\" LF\n" msgstr "" " \"commit 158\" NUL\n" " \"tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9\" LF\n" " \"author Alice 1234567890 -0800\" LF\n" " \"committer Bob 1234567890 -0800\" LF\n" " LF\n" " \"Shakespeare\" LF\n" #. type: Plain text #: ../en/secrets.txt:179 msgid "As before, you can run zpipe or cat-file to see for yourself." msgstr "" "Wie vorhin, kannst Du 'zpipe' oder 'cat-file' benutzen um es für Dich zu " "überprüfen." #. type: Plain text #: ../en/secrets.txt:182 msgid "" "This is the first commit, so there are no parent commits, but later commits " "will always contain at least one line identifying a parent commit." msgstr "" "Das ist der erste 'Commit' gewesen, deshalb gibt es keine Eltern-'Commits'. " "Aber spätere 'Commits' werden immer mindestens eine Zeile enthalten, die den " "Eltern-'Commit' identifiziert." #. type: Plain text #: ../en/secrets.txt:184 msgid "=== Indistinguishable From Magic ===" msgstr "=== Von Magie nicht zu unterscheiden ===" #. type: Plain text #: ../en/secrets.txt:186 msgid "" "Git's secrets seem too simple. It looks like you could mix together a few " "shell scripts and add a dash of C code to cook it up in a matter of hours: a " "melange of basic filesystem operations and SHA1 hashing, garnished with lock " "files and fsyncs for robustness. In fact, this accurately describes the " "earliest versions of Git. Nonetheless, apart from ingenious packing tricks " "to save space, and ingenious indexing tricks to save time, we now know how " "Git deftly changes a filesystem into a database perfect for version control." msgstr "" "Git's Geheimnisse scheinen zu einfach. Es sieht so aus als müsste man nur " "ein paar Kommandozeilenskripte zusammenmixen, einen Schuß C-Code hinzufügen " "und innerhalb ein paar Stunden ist man fertig: eine Mischung von " "grundlegenden Dateisystemoperationen und SHA1-Hash-Berechnungen, garniert " "mit Sperrdateien und Synchronisation für Stabilität. Tatsächlich beschreibt " "dies die früheste Version von Git. Nichtsdestotrotz, abgesehen von " "geschickten Verpackungstricks um Speicherplatz zu sparen und geschickten " "Indizierungstricks um Zeit zu sparen, wissen wir nun, wie Git gewandt ein " "Dateisystem in eine Datenbank verwandelt, das perfekt für eine " "Versionsverwaltung geeignet ist." #. type: Plain text #: ../en/secrets.txt:194 msgid "" "For example, if any file within the object database is corrupted by a disk " "error, then its hash will no longer match, alerting us to the problem. By " "hashing hashes of other objects, we maintain integrity at all levels. " "Commits are atomic, that is, a commit can never only partially record " "changes: we can only compute the hash of a commit and store it in the " "database after we already have stored all relevant trees, blobs and parent " "commits. The object database is immune to unexpected interruptions such as " "power outages." msgstr "" "Angenommen, wenn irgendeine Datei in der Objektdatenbank durch einen " "Laufwerksfehler zerstört wird, dann wird sein SHA1-Hash-Wert nicht mehr mit " "seinem Inhalt übereinstimmen und uns sagen, wo das Problem liegt. Durch " "Bilden von SHA1-Hash-Werten aus den SHA1-Hash-Werten anderer Objekte, " "erreichen wir Integrität auf allen Ebenen. 'Commits' sind elementar, das " "heißt, ein 'Commit' kann niemals nur Teile einer Änderung speichern: wir " "können den SHA1-Hash-Wert eines 'Commits' erst dann berechnen und speichern, " "nachdem wir bereits alle relevanten 'Tree'-Objekte, 'Blob'-Objekte und " "Eltern-'Commits' gespeichert haben. Die Objektdatenbank ist immun gegen " "unerwartete Unterbrechungen wie zum Beispiel einen Stromausfall." #. type: Plain text #: ../en/secrets.txt:205 msgid "" "We defeat even the most devious adversaries. Suppose somebody attempts to " "stealthily modify the contents of a file in an ancient version of a project. " "To keep the object database looking healthy, they must also change the hash " "of the corresponding blob object since it's now a different string of bytes. " "This means they'll have to change the hash of any tree object referencing " "the file, and in turn change the hash of all commit objects involving such a " "tree, in addition to the hashes of all the descendants of these commits. " "This implies the hash of the official head differs to that of the bad " "repository. By following the trail of mismatching hashes we can pinpoint the " "mutilated file, as well as the commit where it was first corrupted." msgstr "" "Wir können sogar den hinterhältigsten Gegnern widerstehen. Stell Dir vor, " "jemand will den Inhalt einer Datei ändern, die in einer älteren Version " "eines Projekt liegt. Um die Objektdatenbank intakt aussehen zu lassen, " "müssten sie außerdem den SHA1-Hash-Wert des korrespondierenden 'Blob'-Objekt " "ändern, da die Datei nun eine geänderte Zeichenfolge enthält. Das heißt " "auch, dass sie jeden SHA1-Hash-Wert der 'Tree'-Objekte ändern müssen, welche " "dieses Objekt referenzieren und demzufolge alle SHA1-Hash-Werte der 'Commit'-" "Objekte, welche diese 'Tree'-Objekte beinhalten, zusätzlich zu allen " "Abkömmlingen dieses 'Commits'. Das bedeutet auch, dass sich der SHA1-Hash-" "Wert des offiziellen HEAD von dem des manipulierten 'Repository' " "unterscheidet. Folgen wir dem Pfad der differierenden SHA1-Hash-Werte, " "finden wir die verstümmelte Datei, wie auch den 'Commit', in dem sie " "erstmals auftauchte." #. type: Plain text #: ../en/secrets.txt:208 msgid "" "In short, so long as the 20 bytes representing the last commit are safe, " "it's impossible to tamper with a Git repository." msgstr "" "Kurz gesagt, so lange die 20 Byte, welche den SHA1-Hash-Wert des letzen " "'Commit' repräsentieren sicher sind, ist es unmöglich ein Git 'Repository' " "zu fälschen." #. type: Plain text #: ../en/secrets.txt:214 msgid "" "What about Git's famous features? Branching? Merging? Tags? Mere details. " "The current head is kept in the file +.git/HEAD+, which contains a hash of a " "commit object. The hash gets updated during a commit as well as many other " "commands. Branches are almost the same: they are files in +.git/refs/heads+. " "Tags too: they live in +.git/refs/tags+ but they are updated by a different " "set of commands." msgstr "" "Was ist mit Git's berühmten Fähigkeiten? 'Branching'? 'Merging'? 'Tags'? Nur " "Kleinigkeiten. Der aktuelle HEAD wird in der Datei +.git/HEAD+ gehalten, " "welche den SHA1-Hash-Wert eines 'Commit'-Objekts enthält. Der SHA1-Hash-Wert " "wird während eines 'Commit' aktualisiert, genauso bei vielen anderen " "Anweisungen. 'Branches' sind fast das selbe: sie sind Dateien in +.git/refs/" "heads+. 'Tags' ebenso: sie stehen in +.git/refs/tags+ aber sie werden durch " "einen Satz anderer Anweisungen aktualisiert." gitmagic-20160304/de/pot/intro.po0000644000175000017500000004115712666307504015675 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010, 2011. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: bennlynn@gmail.com\n" "POT-Creation-Date: 2011-07-03 23:06+0300\n" "PO-Revision-Date: 2011-07-03 23:28+0200\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/intro.txt:2 msgid "== Introduction ==" msgstr "== Einleitung ==" #. type: Plain text #: ../en/intro.txt:4 msgid "" "I'll use an analogy to introduce version control. See http://en.wikipedia." "org/wiki/Revision_control[the Wikipedia entry on revision control] for a " "saner explanation." msgstr "" "Ich benutze eine Analogie um in die Versionsverwaltung einzuführen. Für eine " "vernünftigere Erklärung siehe http://de.wikipedia.org/wiki/Versionsverwaltung" "[den Wikipedia Artikel zur Versionsverwaltung]." #. type: Plain text #: ../en/intro.txt:6 msgid "=== Work is Play ===" msgstr "=== Arbeit ist Spiel ===" #. type: Plain text #: ../en/intro.txt:8 msgid "" "I've played computer games almost all my life. In contrast, I only started " "using version control systems as an adult. I suspect I'm not alone, and " "comparing the two may make these concepts easier to explain and understand." msgstr "" "Ich spiele Computerspiele schon fast mein ganzes Leben. Im Gegensatz dazu " "habe ich erst als Erwachsener damit begonnen Versionsverwaltungssysteme zu " "benutzen. Ich vermute, dass ich damit nicht alleine bin und der Vergleich " "hilft vielleicht dabei die Konzepte einfacher zu erklären und zu verstehen." #. type: Plain text #: ../en/intro.txt:10 msgid "" "Think of editing your code, or document, as playing a game. Once you've made " "a lot of progress, you'd like to save. To do so, you click on the 'Save' " "button in your trusty editor." msgstr "" "Stelle dir das Bearbeiten deines Codes oder deiner Dokumente wie ein " "Computerspiel vor. Wenn du gut voran gekommen bist, willst du das Erreichte " "sichern. Um das zu tun, klickst du auf auf Speichern in deinem vertrauten " "Editor." #. type: Plain text #: ../en/intro.txt:12 msgid "" "But this will overwrite the old version. It's like those old school games " "which only had one save slot: sure you could save, but you could never go " "back to an older state. Which was a shame, because your previous save might " "have been right at an exceptionally fun part of the game that you'd like to " "revisit one day. Or worse still, your current save is in an unwinnable " "state, and you have to start again." msgstr "" "Aber, das überschreibt die vorherige Version. Das ist wie bei den Spielen " "der alten Schule, die nur Speicherplatz für eine Sicherung hatten: " "sicherlich konntest du speichern, aber du konntest nie zu einem älteren " "Stand zurück. Das war eine Schande, denn vielleicht war deine vorherige " "Sicherung an einer außergewöhnlich spannenden Stelle des Spiels, zu der du " "später gerne noch einmal zurückkehren möchtest. Oder noch schlimmer, deine " "aktuelle Sicherung ist in einem nicht lösbaren Stand, dann musst du von ganz " "vorne beginnen." #. type: Plain text #: ../en/intro.txt:14 msgid "=== Version Control ===" msgstr "=== Versionsverwaltung ===" #. type: Plain text #: ../en/intro.txt:16 msgid "" "When editing, you can 'Save As...' a different file, or copy the file " "somewhere first before saving if you want to savour old versions. You can " "compress them too to save space. This is a primitive and labour-intensive " "form of version control. Computer games improved on this long ago, many of " "them providing multiple automatically timestamped save slots." msgstr "" "Beim Editieren kannst du deine Datei durch 'Speichern unter ...' mit einem " "neuen Namen abspeichern oder du kopierst sie vor dem Speichern irgendwo hin " "um die alte Version zu erhalten. Außerdem kannst du sie komprimieren um " "Speicherplatz zu sparen. Das ist eine primitive und mühselige Form der " "Versionsverwaltung. Computerspiele machten das lange Zeit so, viele von " "ihnen hatten automatisch erstellte Sicherungspunkte mit Zeitstempel." #. type: Plain text #: ../en/intro.txt:18 msgid "" "Let's make the problem slightly tougher. Say you have a bunch of files that " "go together, such as source code for a project, or files for a website. Now " "if you want to keep an old version you have to archive a whole directory. " "Keeping many versions around by hand is inconvenient, and quickly becomes " "expensive." msgstr "" "Jetzt lass uns das Problem etwas komplizierter machen. Sagen wir, du hast " "einen Haufen Dateien, die zusammen gehören, z.B. Quellcodes für ein Projekt " "oder Dateien einer Website. Wenn du nun eine alte Version erhalten willst, " "musst du den ganzen Ordner archivieren. Viele Versionen auf diese Art zu " "archivieren ist mühselig und kann sehr schnell teuer werden." #. type: Plain text #: ../en/intro.txt:20 msgid "" "With some computer games, a saved game really does consist of a directory " "full of files. These games hide this detail from the player and present a " "convenient interface to manage different versions of this directory." msgstr "" "Bei einigen Computerspielen bestand ein gesicherter Stand wirklich aus einem " "Ordner voller Dateien. Diese Spiele versteckten die Details vor dem Spieler " "und präsentierten eine bequeme Oberfläche um verschiedene Versionen des " "Ordners zu verwalten." #. type: Plain text #: ../en/intro.txt:22 msgid "" "Version control systems are no different. They all have nice interfaces to " "manage a directory of stuff. You can save the state of the directory every " "so often, and you can load any one of the saved states later on. Unlike most " "computer games, they're usually smart about conserving space. Typically, " "only a few files change from version to version, and not by much. Storing " "the differences instead of entire new copies saves room." msgstr "" "Versionsverwaltungen sind nicht anders. Sie alle haben bequeme " "Schnittstellen um Ordner voller Dateien zu verwalten. Du kannst den Zustand " "des Ordners sichern so oft du willst und du kannst später jeden " "Sicherungspunkt wieder herstellen. Im Gegensatz zu den meisten " "Computerspielen sind sie aber in der Regel dafür ausgelegt sparsam mit dem " "Speicherplatz umzugehen. Normalerweise ändern sich immer nur wenige Dateien " "zwischen zwei Versionen und die Änderungen selbst sind oft nicht groß. Die " "Platzersparnis beruht auf dem Speichern der Unterschiede an Stelle einer " "Kopie der ganzen Datei." #. type: Plain text #: ../en/intro.txt:24 msgid "=== Distributed Control ===" msgstr "=== Verteilte Kontrolle ===" #. type: Plain text #: ../en/intro.txt:26 msgid "" "Now imagine a very difficult computer game. So difficult to finish that many " "experienced gamers all over the world decide to team up and share their " "saved games to try to beat it. Speedruns are real-life examples: players " "specializing in different levels of the same game collaborate to produce " "amazing results." msgstr "" "Nun stell dir ein ganz kompliziertes Computerspiel vor. So schwierig zu " "lösen, dass viele erfahrene Spieler auf der ganzen Welt beschließen sich " "zusammen zu tun und ihre gespeicherten Spielstände auszutauschen um das " "Spiel zu beenden. 'Speedruns' sind Beispiele aus dem echten Leben: Spieler, " "die sich in unterschiedlichen Spielebenen des selben Spiels spezialisiert " "haben, arbeiten zusammen um erstaunliche Ergebnisse zu erzielen." #. type: Plain text #: ../en/intro.txt:28 msgid "" "How would you set up a system so they can get at each other's saves easily? " "And upload new ones?" msgstr "" "Wie würdest du ein System erstellen, bei dem jeder auf einfache Weise die " "Sicherungen der anderen bekommt? Und eigene Sicherungen bereitstellt?" #. type: Plain text #: ../en/intro.txt:30 msgid "" "In the old days, every project used centralized version control. A server " "somewhere held all the saved games. Nobody else did. Every player kept at " "most a few saved games on their machine. When a player wanted to make " "progress, they'd download the latest save from the main server, play a " "while, save and upload back to the server for everyone else to use." msgstr "" "Früher nutzte jedes Projekt eine zentralisierte Versionsverwaltung. Irgendwo " "speicherte ein Server alle gespeicherten Spiele, sonst niemand. Jeder " "Spieler hatte nur ein paar gespeicherte Spiele auf seinem Rechner. Wenn ein " "Spieler einen Fortschritt machen wollte, musste er den aktuellsten Stand vom " "Hauptserver herunterladen, eine Weile spielen, sichern und den Stand dann " "wieder auf den Server laden, damit irgendjemand ihn nutzen kann." #. type: Plain text #: ../en/intro.txt:32 msgid "" "What if a player wanted to get an older saved game for some reason? Maybe " "the current saved game is in an unwinnable state because somebody forgot to " "pick up an object back in level three, and they want to find the latest " "saved game where the game can still be completed. Or maybe they want to " "compare two older saved games to see how much work a particular player did." msgstr "" "Was, wenn ein Spieler aus irgendeinem Grund einen alten Spielstand will? " "Vielleicht ist der aktuell gesicherte Spielstand nicht mehr lösbar, weil " "jemand in der dritten Ebene vergessen hat ein Objekt aufzunehmen und sie " "versuchen den letzten Spielstand zu finden, ab dem das Spiel wieder lösbar " "ist. Oder sie wollen zwei Spielstände vergleichen, um festzustellen wie viel " "ein Spieler geleistet hat." #. type: Plain text #: ../en/intro.txt:34 msgid "" "There could be many reasons to want to see an older revision, but the " "outcome is the same. They have to ask the central server for that old saved " "game. The more saved games they want, the more they need to communicate." msgstr "" "Es gibt viele Gründe warum man einen älteren Stand sehen will, aber das " "Ergebnis ist das selbe. Man muss vom Hauptserver das alte gespeicherte Spiel " "anfordern. Je mehr gespeicherte Spiele benötigt werden, desto mehr " "Kommunikation ist erforderlich." #. type: Plain text #: ../en/intro.txt:36 msgid "" "The new generation of version control systems, of which Git is a member, are " "known as distributed systems, and can be thought of as a generalization of " "centralized systems. When players download from the main server they get " "every saved game, not just the latest one. It's as if they're mirroring the " "central server." msgstr "" "Die neue Generation der Versionsverwaltungssysteme, zu denen Git gehört, " "werden verteilte Systeme genannt und können als eine Verallgemeinerung der " "zentralisierten Systeme verstanden werden. Wenn Spieler vom Hauptserver " "herunterladen, erhalten sie jedes gespeichertes Spiel, nicht nur das zuletzt " "gespeicherte. Es ist als ob der Hauptserver gespiegelt wird." #. type: Plain text #: ../en/intro.txt:38 msgid "" "This initial cloning operation can be expensive, especially if there's a " "long history, but it pays off in the long run. One immediate benefit is that " "when an old save is desired for any reason, communication with the central " "server is unnecessary." msgstr "" "Dieses erste 'Cloning' kann teuer sein, vor allem, wenn eine lange " "Geschichte existiert, aber auf Dauer wird es sich lohnen. Ein unmittelbarer " "Vorteil ist, wenn aus irgendeinem Grund ein älterer Stand benötigt wird, ist " "keine Kommunikation mit dem Hauptserver notwendig." #. type: Plain text #: ../en/intro.txt:40 msgid "=== A Silly Superstition ===" msgstr "=== Ein dummer Aberglaube ===" #. type: Plain text #: ../en/intro.txt:42 msgid "" "A popular misconception is that distributed systems are ill-suited for " "projects requiring an official central repository. Nothing could be further " "from the truth. Photographing someone does not cause their soul to be " "stolen. Similarly, cloning the master repository does not diminish its " "importance." msgstr "" "Ein weit verbreitetes Missverständnis ist, dass verteilte System ungeeignet " "sind für Projekte, die ein offizielles zentrales 'Repository' benötigen. " "Nichts könnte weiter von der Wahrheit entfernt sein. Jemanden zu " "fotografieren stiehlt nicht dessen Seele. Genauso wenig setzt das 'Clonen' " "des zentralen 'Repository' dessen Bedeutung herab." #. type: Plain text #: ../en/intro.txt:44 msgid "" "A good first approximation is that anything a centralized version control " "system can do, a well-designed distributed system can do better. Network " "resources are simply costlier than local resources. While we shall later see " "there are drawbacks to a distributed approach, one is less likely to make " "erroneous comparisons with this rule of thumb." msgstr "" "Eine gute erste Annäherung ist, dass alles was eine zentralisierte " "Versionsverwaltung kann, ein gut durchdachtes verteiltes System besser kann. " "Netzwerkressourcen sind einfach teurer als lokale Ressourcen. Auch wenn wir " "später Nachteile beim verteilten Ansatz sehen werden, ist man mit dieser " "Faustregel weniger anfällig für falsche Vergleiche." #. type: Plain text #: ../en/intro.txt:48 msgid "" "A small project may only need a fraction of the features offered by such a " "system, but using systems that scale poorly for tiny projects is like using " "Roman numerals for calculations involving small numbers." msgstr "" "Ein kleines Projekt mag nur einen Bruchteil der Möglichkeiten benötigen, die " "so ein System bietet. Aber deshalb ein einfacheres, schlecht erweiterbares " "System zu benutzen, ist wie römische Ziffern zum Rechnen mit kleinen Zahlen " "zu verwenden." #. type: Plain text #: ../en/intro.txt:50 msgid "" "Moreover, your project may grow beyond your original expectations. Using Git " "from the outset is like carrying a Swiss army knife even though you mostly " "use it to open bottles. On the day you desperately need a screwdriver you'll " "be glad you have more than a plain bottle-opener." msgstr "" "Außerdem könnte dein Projekt weit über die ursprünglichen Erwartungen " "hinauswachsen. Git von Anfang an zu benutzen, ist wie ein Schweizer " "Taschenmesser mit sich zu tragen, auch wenn damit meistens nur Flaschen " "geöffnet werden. Eines Tages brauchst du vielleicht dringend einen " "Schraubendreher, dann bist du froh mehr als nur einen einfachen " "Flaschenöffner bei dir zu haben." #. type: Plain text #: ../en/intro.txt:52 msgid "=== Merge Conflicts ===" msgstr "=== 'Merge' Konflikte ===" #. type: Plain text #: ../en/intro.txt:54 msgid "" "For this topic, our computer game analogy becomes too thinly stretched. " "Instead, let us again consider editing a document." msgstr "" "Für diesen Punkt ist unsere Computerspielanalogie ungeeignet. Stattdessen " "stellen wir uns wieder vor, wir editieren ein Dokument." #. type: Plain text #: ../en/intro.txt:56 msgid "" "Suppose Alice inserts a line at the beginning of a file, and Bob appends one " "at the end of his copy. They both upload their changes. Most systems will " "automatically deduce a reasonable course of action: accept and merge their " "changes, so both Alice's and Bob's edits are applied." msgstr "" "Stell dir vor, Alice fügt eine Zeile am Dateianfang hinzu und Bob eine am " "Dateiende. Beide laden ihre Änderungen hoch. Die meisten Systeme wählen " "automatisch eine vernünftige Vorgehensweise: akzeptiere beide Änderungen und " "füge sie zusammen, damit fließen beide Änderungen in das Dokument mit ein." #. type: Plain text #: ../en/intro.txt:58 msgid "" "Now suppose both Alice and Bob have made distinct edits to the same line. " "Then it is impossible to proceed without human intervention. The second " "person to upload is informed of a _merge conflict_, and must choose one edit " "over another, or revise the line entirely." msgstr "" "Nun stell dir vor beide, Alice und Bob, machen Änderungen in der selben " "Zeile. Dann ist es unmöglich ohne menschlichen Eingriff fortzufahren. Die " "zweite Person, welche die Datei hoch lädt, wird über einen _'Merge' " "Konflikt_ informiert und muss entscheiden, welche Änderung übernommen wird " "oder die ganze Zeile überarbeiten. " #. type: Plain text #: ../en/intro.txt:59 msgid "" "More complex situations can arise. Version control systems handle the " "simpler cases themselves, and leave the difficult cases for humans. Usually " "their behaviour is configurable." msgstr "" "Es können noch weitaus kompliziertere Situationen entstehen. " "Versionsverwaltungssysteme behandeln die einfacheren Fälle selbst und " "überlassen die schwierigen uns Menschen. Üblicherweise ist deren Verhalten " "einstellbar." gitmagic-20160304/de/pot/preface.po0000644000175000017500000002625612666307504016152 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010, 2011. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: bennlynn@gmail.com\n" "POT-Creation-Date: 2011-07-03 23:06+0300\n" "PO-Revision-Date: 2011-07-10 17:08+0200\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/preface.txt:4 msgid "= Git Magic = Ben Lynn August 2007" msgstr "= Git Magic = Ben Lynn August 2007" #. type: Plain text #: ../en/preface.txt:6 msgid "== Preface ==" msgstr "== Vorwort ==" #. type: Plain text #: ../en/preface.txt:8 msgid "" "http://git.or.cz/[Git] is a version control Swiss army knife. A reliable " "versatile multipurpose revision control tool whose extraordinary flexibility " "makes it tricky to learn, let alone master." msgstr "" "http://git.or.cz/[Git] ist wie ein schweizer Taschenmesser für " "Versionsverwaltung. Ein zuverlässiges, vielseitiges Mehrzweck-" "Versionsverwaltungswerkzeug, dessen außergewöhnliche Flexibilität es " "schwierig zu erlernen macht, ganz zu schweigen davon es zu meistern." #. type: Plain text #: ../en/preface.txt:10 msgid "" "As Arthur C. Clarke observed, any sufficiently advanced technology is " "indistinguishable from magic. This is a great way to approach Git: newbies " "can ignore its inner workings and view Git as a gizmo that can amaze friends " "and infuriate enemies with its wondrous abilities." msgstr "" "Wie Arthur C. Clarke festgestellt hat, ist jede hinreichend fortschrittliche " "Technologie von Magie nicht zu unterscheiden. Das ist ein großartiger Ansatz " "um an Git heranzugehen: Anfänger können seine inneren Mechanismen ignorieren " "und Git als ein Ding ansehen, das mit seinen erstaunlichen Fähigkeiten " "Freunde verzückt und Gegner zur Weißglut bringt." #. type: Plain text #: ../en/preface.txt:12 msgid "" "Rather than go into details, we provide rough instructions for particular " "effects. After repeated use, gradually you will understand how each trick " "works, and how to tailor the recipes for your needs." msgstr "" "Anstatt die Details aufzudecken, bieten wir grobe Anweisungen für die " "jeweiligen Funktionen. Bei regelmäßiger Anwendung wirst Du allmählich " "verstehen, wie jeder Trick funktioniert und wie Du die Rezepte auf Deinen " "Bedarf zuschneiden kannst." #. type: Plain text #: ../en/preface.txt:14 msgid ".Translations" msgstr ".Übersetzungen" #. type: Bullet: ' - ' #: ../en/preface.txt:22 msgid "" "link:/\\~blynn/gitmagic/intl/zh_cn/[Simplified Chinese]: by JunJie, Meng and " "JiangWei. Converted to link:/~blynn/gitmagic/intl/zh_tw/[Traditional " "Chinese] via +cconv -f UTF8-CN -t UTF8-TW+." msgstr "" "link:/\\~blynn/gitmagic/intl/zh_cn/[Vereinfachtes Chinesisch]: von JunJie, " "Meng und JiangWei. Zu link:/~blynn/gitmagic/intl/zh_tw/[Traditionellem " "Chinesisch] konvertiert via +cconv -f UTF8-CN -t UTF8-TW+." #. type: Bullet: ' - ' #: ../en/preface.txt:22 msgid "" "link:/~blynn/gitmagic/intl/fr/[French]: by Alexandre Garel, Paul Gaborit, " "and Nicolas Deram. Also hosted at http://tutoriels.itaapy.com/[itaapy]." msgstr "" "link:/~blynn/gitmagic/intl/fr/[Französich]: von Alexandre Garel, Paul " "Gaborit, und Nicolas Deram. Auch gehostet unter http://tutoriels.itaapy.com/" "[itaapy]." #. type: Bullet: ' - ' #: ../en/preface.txt:22 msgid "" "link:/~blynn/gitmagic/intl/de/[German]: by Benjamin Bellee and Armin " "Stebich; also http://gitmagic.lordofbikes.de/[hosted on Armin's website]." msgstr "" "link:/~blynn/gitmagic/intl/de/[Deutsch]: von Benjamin Bellee und Armin " "Stebich; Auch gehostet unter http://gitmagic.lordofbikes.de/[Armin's " "Website]." #. type: Bullet: ' - ' #: ../en/preface.txt:22 msgid "" "http://www.slideshare.net/slide_user/magia-git[Portuguese]: by Leonardo " "Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt" "[ODT version]]." msgstr "" "http://www.slideshare.net/slide_user/magia-git[Portugiesisch]: von Leonardo " "Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt" "[ODT-Version]]." #. type: Bullet: ' - ' #: ../en/preface.txt:22 msgid "" "link:/~blynn/gitmagic/intl/ru/[Russian]: by Tikhon Tarnavsky, Mikhail " "Dymskov, and others." msgstr "" "link:/~blynn/gitmagic/intl/ru/[Russisch]: von Tikhon Tarnavsky, Mikhail " "Dymskov, und anderen." #. type: Bullet: ' - ' #: ../en/preface.txt:22 msgid "" "link:/~blynn/gitmagic/intl/es/[Spanish]: by Rodrigo Toledo and Ariset " "Llerena Tapia." msgstr "" "link:/~blynn/gitmagic/intl/es/[Spanisch]: von Rodrigo Toledo und Ariset " "Llerena Tapia." #. type: Bullet: ' - ' #: ../en/preface.txt:22 msgid "" "link:/~blynn/gitmagic/intl/vi/[Vietnamese]: by Trần Ngọc Quân; also http://" "vnwildman.users.sourceforge.net/gitmagic.html[hosted on his website]." msgstr "" "link:/~blynn/gitmagic/intl/vi/[Vietnamesisch]: von Trần Ngọc Quân; Auch " "gehostet unter http://vnwildman.users.sourceforge.net/gitmagic.html[seiner " "Website]." #. type: Plain text #: ../en/preface.txt:24 msgid ".Other Editions" msgstr ".Andere Ausgaben" #. type: Bullet: ' - ' #: ../en/preface.txt:29 msgid "link:book.html[Single webpage]: barebones HTML, with no CSS." msgstr "link:book.html[Einzelne Webseite]: reines HTML, ohne CSS." #. type: Bullet: ' - ' #: ../en/preface.txt:29 msgid "link:book.pdf[PDF file]: printer-friendly." msgstr "link:book.pdf[PDF Datei]: druckerfreundlich." #. type: Bullet: ' - ' #: ../en/preface.txt:29 msgid "" "http://packages.debian.org/gitmagic[Debian package], http:://packages.ubuntu." "com/gitmagic[Ubuntu package]: get a fast and local copy of this site. Handy " "http://csdcf.stanford.edu/status/[when this server is offline]." msgstr "" "http://packages.debian.org/gitmagic[Debian Packet], http:://packages.ubuntu." "com/gitmagic[Ubuntu Packet]: Für eine schnelle und lokale Kopie dieser " "Seite. Praktisch, http://csdcf.stanford.edu/status/[wenn dieser Server " "offline ist]." #. type: Bullet: ' - ' #: ../en/preface.txt:29 msgid "" "http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Physical book " "[Amazon.com]]: 64 pages, 15.24cm x 22.86cm, black and white. Handy when " "there is no electricity." msgstr "" "http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Gedrucktes Buch " "[Amazon.com]]: 64 Seiten, 15.24cm x 22.86cm, schwarz/weiß. Praktisch, wenn " "es keinen Strom gibt." #. type: Plain text #: ../en/preface.txt:31 msgid "=== Thanks! ===" msgstr "=== Danke! ===" #. type: Plain text #: ../en/preface.txt:35 msgid "" "I'm humbled that so many people have worked on translations of these pages. " "I greatly appreciate having a wider audience because of the efforts of those " "named above." msgstr "" "Ich bin erstaunt, daß so viele Leute an der Übersetzung dieser Seiten " "gearbeitet haben. Ich weiß es zu würdigen, daß ich, dank der Bemühungen der " "oben genannten, einen größeren Leserkreis erreiche." #. type: Plain text #: ../en/preface.txt:37 msgid "" "Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, " "Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith " "Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, " "Sébastien Hinderer, Thomas Miedema, Joe Malin, and Tyler Breisacher " "contributed corrections and improvements." msgstr "" "Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, " "Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith " "Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, " "Sébastien Hinderer, Thomas Miedema, Joe Malin, und Tyler Breisacher haben " "Korrekturen und Verbesserungen beigesteuert." #. type: Plain text #: ../en/preface.txt:40 msgid "" "François Marier maintains the Debian package originally created by Daniel " "Baumann." msgstr "" "François Marier unterhält das Debian Packet, das ursprünglich von Daniel " "Baumann erstellt wurde." #. type: Plain text #: ../en/preface.txt:43 msgid "" "My gratitude goes to many others for your support and praise. I'm tempted to " "quote you here, but it might raise expectations to ridiculous heights." msgstr "" "Meine Dankbarkeit gilt auch vielen anderen für deren Unterstützung und Lob. " "Ich war versucht euch hier alle aufzuzählen, aber das könnte Erwartungen in " "unermesslichem Umfang wecken." #. type: Plain text #: ../en/preface.txt:45 msgid "" "If I've left you out by mistake, please tell me or just send me a patch!" msgstr "" "Wenn ich Dich versehentlich vergessen habe, sag mir bitte Bescheid oder " "schicke mir einfach einen Patch!" #. type: Plain text #: ../en/preface.txt:47 msgid ".Free Git hosting" msgstr ".Kostenloses Git Hosting" #. type: Bullet: ' - ' #: ../en/preface.txt:51 msgid "" "http://repo.or.cz/[http://repo.or.cz/] hosts free projects. The first Git " "hosting site. Founded and maintained by one of the earliest Git developers." msgstr "" "http://repo.or.cz/[http://repo.or.cz/] hostet freie Projekte. Die allererste " "Git Hosting Seite. Gegründet und betrieben von einem der ersten Git " "Entwickler." #. type: Bullet: ' - ' #: ../en/preface.txt:51 msgid "" "http://gitorious.org/[http://gitorious.org/] is another Git hosting site " "aimed at open-source projects." msgstr "" "http://gitorious.org/[http://gitorious.org/] ist eine andere Git Hosting " "Seite, bevorzugt für Open-Source Projekte." #. type: Bullet: ' - ' #: ../en/preface.txt:51 msgid "" "http://github.com/[http://github.com/] hosts open-source projects for free, " "and private projects for a fee." msgstr "" "http://github.com/[http://github.com/] hostet Open-Source Projekte kostenlos " "und geschlossene Projekte gegen Gebühr." #. type: Plain text #: ../en/preface.txt:53 msgid "Many thanks to each of these sites for hosting this guide." msgstr "Vielen Dank an jede dieser Seiten für das Hosten dieser Anleitung." #. type: Plain text #: ../en/preface.txt:55 msgid "=== License ===" msgstr "=== Lizenz ===" #. type: Plain text #: ../en/preface.txt:58 msgid "" "This guide is released under http://www.gnu.org/licenses/gpl-3.0.html[the " "GNU General Public License version 3]. Naturally, the source is kept in a " "Git repository, and can be obtained by typing:" msgstr "" "Diese Anleitung ist veröffnetlicht unter http://www.gnu.org/licenses/gpl-3.0." "html[der GNU General Public License Version 3]. Natürlich wird der Quelltext " "in einem Git 'Repository' gehalten und kann abgerufen werden durch:" #. type: Plain text #: ../en/preface.txt:60 #, no-wrap msgid " $ git clone git://repo.or.cz/gitmagic.git # Creates \"gitmagic\" directory.\n" msgstr " $ git clone git://repo.or.cz/gitmagic.git # Erstellt \"gitmagic\" Verzeichnis.\n" #. type: Plain text #: ../en/preface.txt:62 msgid "or from one of the mirrors:" msgstr "oder von einem der Mirrorserver:" #. type: Plain text #: ../en/preface.txt:64 #, no-wrap msgid "" " $ git clone git://github.com/blynn/gitmagic.git\n" " $ git clone git://gitorious.org/gitmagic/mainline.git\n" msgstr "" " $ git clone git://github.com/blynn/gitmagic.git\n" " $ git clone git://gitorious.org/gitmagic/mainline.git\n" gitmagic-20160304/de/pot/drawbacks.po0000644000175000017500000004565212666307504016507 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010, 2011. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: bennlynn@gmail.com\n" "POT-Creation-Date: 2010-10-30 08:21+0300\n" "PO-Revision-Date: 2011-07-10 17:25+0200\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/drawbacks.txt:2 msgid "== Appendix A: Git Shortcomings ==" msgstr "== Anhang A: Git's Mängel ==" #. type: Plain text #: ../en/drawbacks.txt:4 msgid "" "There are some Git issues I've swept under the carpet. Some can be handled " "easily with scripts and hooks, some require reorganizing or redefining the " "project, and for the few remaining annoyances, one will just have to wait. " "Or better yet, pitch in and help!" msgstr "" "Ein paar Git-Probleme habe ich bisher unter den Teppich gekehrt. Einige " "lassen sich einfach mit Skripten und 'Hooks' lösen, andere erfordern eine " "Reorganisation oder Neudefinition des gesamten Projekt und für die wenigen " "verbleibenden Beeinträchtigungen kannst Du nur auf eine Lösung warten. Oder " "noch besser, anpacken und mithelfen." #. type: Plain text #: ../en/drawbacks.txt:6 msgid "=== SHA1 Weaknesses ===" msgstr "=== SHA1 Schwäche ===" #. type: Plain text #: ../en/drawbacks.txt:11 msgid "" "As time passes, cryptographers discover more and more SHA1 weaknesses. " "Already, finding hash collisions is feasible for well-funded organizations. " "Within years, perhaps even a typical PC will have enough computing power to " "silently corrupt a Git repository." msgstr "" "Mit der Zeit entdecken Kryptographen immer mehr Schwächen an SHA1. Schon " "heute wäre es technisch machbar für finanzkräftige Unternehmen Hash-" "Kollisionen zu finden. In ein paar Jahren hat vielleicht schon ein ganz " "normaler Heim-PC ausreichend Rechenleistung um ein Git 'Reopsitory' " "unbemerkt zu korrumpieren." #. type: Plain text #: ../en/drawbacks.txt:14 msgid "" "Hopefully Git will migrate to a better hash function before further research " "destroys SHA1." msgstr "" "Hoffentlich stellt Git auf eine bessere Hash Funktion um, bevor die " "Forschung SHA1 komplett unnütz macht." #. type: Plain text #: ../en/drawbacks.txt:16 msgid "=== Microsoft Windows ===" msgstr "=== Microsoft Windows ===" #. type: Plain text #: ../en/drawbacks.txt:18 msgid "Git on Microsoft Windows can be cumbersome:" msgstr "Git unter Microsoft Windows kann frustrierend sein:" #. type: Plain text #: ../en/drawbacks.txt:20 msgid "" "- http://cygwin.com/[Cygwin], a Linux-like environment for Windows, contains " "http://cygwin.com/packages/git/[a Windows port of Git]." msgstr "" "- http://cygwin.com/[Cygwin], eine Linux ähnliche Umgebung für Windows, " "enthält http://cygwin.com/packages/git/[eine Windows Portierung von Git]." #. type: Plain text #: ../en/drawbacks.txt:22 msgid "" "- http://code.google.com/p/msysgit/[Git on MSys] is an alternative requiring " "minimal runtime support, though a few of the commands need some work." msgstr "" "- http://code.google.com/p/msysgit/[Git unter MSys] ist eine Alternative, " "die sehr wenig Laufzeitunterstützung erfordert, jedoch bedürfen einige " "Kommandos noch einer Überarbeitung." #. type: Plain text #: ../en/drawbacks.txt:24 msgid "=== Unrelated Files ===" msgstr "=== Dateien ohne Bezug ===" #. type: Plain text #: ../en/drawbacks.txt:26 msgid "" "If your project is very large and contains many unrelated files that are " "constantly being changed, Git may be disadvantaged more than other systems " "because single files are not tracked. Git tracks changes to the whole " "project, which is usually beneficial." msgstr "" "Wenn Dein Projekt sehr groß ist und viele Dateien enthält, die in keinem " "direkten Bezug stehen, trotzdem aber häufig geändert werden, kann Git " "nachteiliger sein als andere Systeme, weil es keine einzelnen Dateien " "überwacht. Git überwacht immer das ganze Projekt, was normalerweise schon " "von Vorteil ist." #. type: Plain text #: ../en/drawbacks.txt:28 msgid "" "A solution is to break up your project into pieces, each consisting of " "related files. Use *git submodule* if you still want to keep everything in a " "single repository." msgstr "" "Eine Lösung ist es, Dein Projekt in kleinere Stücke aufzuteilen, von denen " "jedes nur die in Beziehung stehenden Dateien enthält. Benutze *git " "submodule* wenn Du trotzdem alles in einem einzigen 'Repository' halten " "willst." #. type: Plain text #: ../en/drawbacks.txt:30 msgid "=== Who's Editing What? ===" msgstr "=== Wer macht was? ===" #. type: Plain text #: ../en/drawbacks.txt:32 msgid "" "Some version control systems force you to explicitly mark a file in some way " "before editing. While this is especially annoying when this involves talking " "to a central server, it does have two benefits:" msgstr "" "Einige Versionsverwaltungssysteme zwingen Dich explizit eine Datei auf " "irgendeine Weise für die Bearbeitung zu kennzeichnen. Obwohl es extrem " "lästig ist, wenn es die Kommunikation mit einem zentralen Server erfordert, " "so hat es doch zwei Vorteile:" #. type: Bullet: ' 1. ' #: ../en/drawbacks.txt:34 msgid "Diffs are quick because only the marked files need be examined." msgstr "" "Unterschiede sind schnell gefunden, weil nur die markierten Dateien " "untersucht werden müssen." #. type: Bullet: ' 2. ' #: ../en/drawbacks.txt:36 msgid "" "One can discover who else is working on the file by asking the central " "server who has marked it for editing." msgstr "" "Jeder kann herausfinden wer sonst gerade an einer Datei arbeitet, indem er " "beim zentralen Server anfragt, wer die Datei zum Bearbeiten markiert hat." #. type: Plain text #: ../en/drawbacks.txt:38 msgid "" "With appropriate scripting, you can achieve the same with Git. This requires " "cooperation from the programmer, who should execute particular scripts when " "editing a file." msgstr "" "Mit geeigneten Skripten kannst Du das auch mit Git hinkriegen. Das erfordert " "aber die Mitarbeit der Programmierer, denn sie müssen die Skripte auch " "aufrufen, wenn sie eine Datei bearbeiten." #. type: Plain text #: ../en/drawbacks.txt:40 msgid "=== File History ===" msgstr "=== Dateihistorie ===" #. type: Plain text #: ../en/drawbacks.txt:42 msgid "" "Since Git records project-wide changes, reconstructing the history of a " "single file requires more work than in version control systems that track " "individual files." msgstr "" "Da Git die Änderungen über das gesamte Projekt aufzeichnet, erfordert die " "Rekonstruktion des Verlaufs einer einzelnen Datei mehr Aufwand als in " "Versionsverwaltungssystemen die einzelne Dateien überwachen." #. type: Plain text #: ../en/drawbacks.txt:44 msgid "" "The penalty is typically slight, and well worth having as other operations " "are incredibly efficient. For example, `git checkout` is faster than `cp -" "a`, and project-wide deltas compress better than collections of file-based " "deltas." msgstr "" "Die Nachteile sind üblicherweise gering und werden gern in Kauf genommen, da " "andere Operationen dafür unglaublich effizient sind. Zum Beispiel ist `git " "checkout` schneller als `cp -a` und projektweite Unterschiede sind besser zu " "komprimieren als eine Sammlung von Änderungen auf Dateibasis." #. type: Plain text #: ../en/drawbacks.txt:46 msgid "=== Initial Clone ===" msgstr "=== Der erster Klon ===" #. type: Plain text #: ../en/drawbacks.txt:48 msgid "" "Creating a clone is more expensive than checking out code in other version " "control systems when there is a lengthy history." msgstr "" "Einen Klon zu erstellen ist aufwendiger als in anderen " "Versionsverwaltungssystemen, wenn ein längerer Verlauf existiert." #. type: Plain text #: ../en/drawbacks.txt:50 msgid "" "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." msgstr "" "Der initiale Aufwand lohnt sich aber auf längere Sicht, da die meisten " "zukünftigen Operationen dann schnell und offline erfolgen. Trotzdem gibt es " "Situationen, in denen es besser ist einen oberflächlichen Klon mit der `--" "depth` Option zu erstellen. Das geht wesentlich schneller, aber der " "resultierende Klon hat nur eingeschränkte Funktionalität." #. type: Plain text #: ../en/drawbacks.txt:52 msgid "=== Volatile Projects ===" msgstr "=== Unbeständige Projekte ===" #. type: Plain text #: ../en/drawbacks.txt:54 msgid "" "Git was written to be fast with respect to the size of the changes. Humans " "make small edits from version to version. A one-liner bugfix here, a new " "feature there, emended comments, and so forth. But if your files are " "radically different in successive revisions, then on each commit, your " "history necessarily grows by the size of your whole project." msgstr "" "Git wurde geschrieben um schnell zu sein, im Hinblick auf die Größe der " "Änderungen. Leute machen kleine Änderungen von Version zu Version. Ein " "einzeiliger Bugfix hier, eine neue Funktion da, verbesserte Kommentare und " "so weiter. Aber wenn sich Deine Dateien zwischen aufeinanderfolgenden " "Versionen gravierend ändern, dann wird zwangsläufig mit jedem 'Commit' Dein " "Verlauf um die Größe des gesamten Projekts wachsen. " #. type: Plain text #: ../en/drawbacks.txt:56 msgid "" "There is nothing any version control system can do about this, but standard " "Git users will suffer more since normally histories are cloned." msgstr "" "Es gibt nichts, was irgendein Versionsverwaltungssystem dagegen machen kann, " "aber der Standard Git Anwender leidet mehr darunter, weil normalerweise der " "ganze Verlauf geklont wird." #. type: Plain text #: ../en/drawbacks.txt:58 msgid "" "The reasons why the changes are so great should be examined. Perhaps file " "formats should be changed. Minor edits should only cause minor changes to at " "most a few files." msgstr "" "Die Ursachen für die großen Unterschiede sollten ermittelt werden. " "Vielleicht können Dateiformate geändert werden. Kleinere Bearbeitungen " "sollten auch nur minimale Änderungen an so wenig Dateien wie möglich " "bewirken." #. type: Plain text #: ../en/drawbacks.txt:60 msgid "" "Or perhaps a database or backup/archival solution is what is actually being " "sought, not a version control system. For example, version control may be " "ill-suited for managing photos periodically taken from a webcam." msgstr "" "Vielleicht ist eher eine Datenbank oder Sicherungs-/Archivierungslösung " "gesucht, nicht ein Versionsverwaltungssystem. Ein Versionsverwaltungssystem " "zum Beispiel ist eine ungeeignete Lösung um Fotos zu verwalten, die " "periodisch von einer Webcam gemacht werden." #. type: Plain text #: ../en/drawbacks.txt:62 msgid "" "If the files really must be constantly morphing and they really must be " "versioned, a possibility is to use Git in a centralized fashion. One can " "create shallow clones, which checks out little or no history of the project. " "Of course, many Git tools will be unavailable, and fixes must be submitted " "as patches. This is probably fine as it's unclear why anyone would want the " "history of wildly unstable files." msgstr "" "Wenn die Dateien sich tatsächlich konstant verändern und sie wirklich " "versioniert werden müssen, ist es eine Möglichkeit Git in zentralisierter " "Form zu verwenden. Jeder kann oberflächliche Klone erstellen, die nur wenig " "oder gar nichts vom Verlauf des Projekts enthalten. Natürlich sind dann " "viele Git Funktionen nicht verfügbar und Änderungen müssen als 'Patches' " "übermittelt werden. Das funktioniert wahrscheinlich ganz gut, wenn auch " "unklar ist, warum jemand die Versionsgeschichte von wahnsinnig instabilen " "Dateien braucht." #. type: Plain text #: ../en/drawbacks.txt:64 msgid "" "Another example is a project depending on firmware, which takes the form of " "a huge binary file. The history of the firmware is uninteresting to users, " "and updates compress poorly, so firmware revisions would unnecessarily blow " "up the size of the repository." msgstr "" "Ein anderes Beispiel ist ein Projekt, das von Firmware abhängig ist, welche " "die Form einer großen Binärdatei annimmt. Der Verlauf der Firmware " "interessiert den Anwender nicht und Änderungen lassen sich schlecht " "komprimieren, so blähen Firmwarerevisionen die Größe des 'Repository' " "unnötig auf." #. type: Plain text #: ../en/drawbacks.txt:66 msgid "" "In this case, the source code should be stored in a Git repository, and the " "binary file should be kept separately. To make life easier, one could " "distribute a script that uses Git to clone the code, and rsync or a Git " "shallow clone for the firmware." msgstr "" "In diesem Fall sollte der Quellcode der Firmware in einem Git 'Repository' " "gehalten werden und die Binärdatei außerhalb des Projekts. Um das Leben zu " "vereinfachen, könnte jemand ein Skript erstellen, das Git benutzt um den " "Quellcode zu klonen und 'rsync' oder einen oberflächlichen Klon für die " "Firmware." #. type: Plain text #: ../en/drawbacks.txt:68 msgid "=== Global Counter ===" msgstr "=== Globaler Zähler ===" #. type: Plain text #: ../en/drawbacks.txt:70 msgid "" "Some centralized version control systems maintain a positive integer that " "increases when a new commit is accepted. Git refers to changes by their " "hash, which is better in many circumstances." msgstr "" "Verschiedene Versionsverwaltungssysteme unterhalten einen Zähler, der mit " "jedem 'Commit' erhöht wird. Git referenziert Änderungen anhand ihres SHA1-" "Hash, was in vielen Fällen besser ist." #. type: Plain text #: ../en/drawbacks.txt:72 msgid "" "But some people like having this integer around. Luckily, it's easy to write " "scripts so that with every update, the central Git repository increments an " "integer, perhaps in a tag, and associates it with the hash of the latest " "commit." msgstr "" "Aber einige Leute sind diesen Zähler gewöhnt. Zum Glück ist es einfach, " "Skripte zu schreiben, sodass mit jedem Update das zentrale Git 'Repository' " "einen Zähler erhöht. Vielleicht in Form eines 'Tags', der mit dem SHA1-Hash " "des letzten 'Commit' verknüpft ist." #. type: Plain text #: ../en/drawbacks.txt:74 msgid "" "Every clone could maintain such a counter, but this would probably be " "useless, since only the central repository and its counter matters to " "everyone." msgstr "" "Jeder Klon könnte einen solchen Zähler bereitstellen, aber der wäre " "vermutlich nutzlos, denn nur der Zähler des zentralen 'Repository' ist für " "alle relevant." #. type: Plain text #: ../en/drawbacks.txt:76 msgid "=== Empty Subdirectories ===" msgstr "=== Leere Unterverzeichnisse ===" #. type: Plain text #: ../en/drawbacks.txt:78 msgid "" "Empty subdirectories cannot be tracked. Create dummy files to work around " "this problem." msgstr "" "Leere Unterverzeichnisse können nicht überwacht werden. Erstelle eine Dummy-" "Datei um dieses Problem zu umgehen." #. type: Plain text #: ../en/drawbacks.txt:80 msgid "" "The current implementation of Git, rather than its design, is to blame for " "this drawback. With luck, once Git gains more traction, more users will " "clamour for this feature and it will be implemented." msgstr "" "Die aktuelle Implementierung von Git, weniger sein Design, ist " "verantwortlich für diesen Pferdefuß. Mit etwas Glück, wenn Git's Verbreitung " "zunimmt und mehr Anwender nach dieser Funktion verlangen, wird sie " "vielleicht implementiert." #. type: Plain text #: ../en/drawbacks.txt:82 msgid "=== Initial Commit ===" msgstr "=== Initialer 'Commit' ===" #. type: Plain text #: ../en/drawbacks.txt:84 msgid "" "A stereotypical computer scientist counts from 0, rather than 1. " "Unfortunately, with respect to commits, git does not adhere to this " "convention. Many commands are unfriendly before the initial commit. " "Additionally, some corner cases must be handled specially, such as rebasing " "a branch with a different initial commit." msgstr "" "Ein klischeehafter Computerwissenschaftler zählt von 0 statt von 1. Leider, " "bezogen auf 'Commits', hält sich Git nicht an diese Konvention. Viele " "Kommandos sind mürrisch vor dem intialen 'Commit'. Zusätzlich müssen " "verschiedene Grenzfälle speziell behandelt werden, wie der 'Rebase' eines " "'Branch' mit einem abweichenden initialen 'Commit'." #. type: Plain text #: ../en/drawbacks.txt:86 msgid "" "Git would benefit from defining the zero commit: as soon as a repository is " "constructed, HEAD would be set to the string consisting of 20 zero bytes. " "This special commit represents an empty tree, with no parent, at some time " "predating all Git repositories." msgstr "" "Git würde davon provitieren, einen Null-'Commit' zu definieren: sofort nach " "dem Erstellen eines 'Repository' wird der 'HEAD' auf eine Zeichenfolge von " "20 Null-Bytes gesetzt. Dieser spezielle 'Commit' repräsentiert einen leeren " "'Tree', ohne Eltern, irgendwann vielleicht der Vorfahr aller Git " "'Repositories'." #. type: Plain text #: ../en/drawbacks.txt:88 msgid "" "Then running git log, for example, would inform the user that no commits " "have been made yet, instead of exiting with a fatal error. Similarly for " "other tools." msgstr "" "Würde dann zum Beispiel *git log* ausgeführt, würde der Anwender darüber " "informiert, daß noch keine 'Commits' gemacht wurden, anstelle mit einem " "fatalen Fehler zu beenden. Das gilt stellvertretenden für andere Anweisungen." #. type: Plain text #: ../en/drawbacks.txt:90 msgid "Every initial commit is implicitly a descendant of this zero commit." msgstr "" "Jeder initiale 'Commit' ist dann stillschweigend ein Abkömmling dieses " "Null-'Commits'." #. type: Plain text #: ../en/drawbacks.txt:92 msgid "" "However there are some problem cases unfortunately. If several branches with " "different initial commits are merged together, then rebasing the result " "requires substantial manual intervention." msgstr "" "Leider gibt es noch ein paar Problemfälle. Wenn mehrere 'Branches' mit " "unterschiedlichen initialen 'Commits' zusammengeführt und dann ein 'Rebase' " "gemacht wird, ist ein manuelles Eingreifen erforderlich." #. type: Plain text #: ../en/drawbacks.txt:94 msgid "=== Interface Quirks ===" msgstr "=== Eigenarten der Anwendung ===" #. type: Plain text #: ../en/drawbacks.txt:97 msgid "" "For commits A and B, the meaning of the expressions \"A..B\" and \"A...B\" " "depends on whether the command expects two endpoints or a range. See *git " "help diff* and *git help rev-parse*." msgstr "" "Für die 'Commits' A und B, hängt die Bedeutung der Ausdrücke \"A..B\" und " "\"A...B\" davon ab, ob eine Anweisung zwei Endpunkte erwartet oder einen " "Bereich. Siehe *git help diff* und *git help rev-parse*." gitmagic-20160304/de/pot/branch.po0000644000175000017500000010014312666307504015766 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010, 2011. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: bennlynn@gmail.com\n" "POT-Creation-Date: 2011-07-03 23:06+0300\n" "PO-Revision-Date: 2011-07-03 23:23+0200\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/branch.txt:2 msgid "== Branch Wizardry ==" msgstr "== 'Branch'-Magie ==" #. type: Plain text #: ../en/branch.txt:4 msgid "" "Instant branching and merging are the most lethal of Git's killer features." msgstr "" "Unverzügliches 'Branchen' und 'Mergen' sind die hervorstechenden " "Eigenschaften von Git." #. type: Plain text #: ../en/branch.txt:8 #, no-wrap msgid "" "*Problem*: External factors inevitably necessitate context switching. A severe\n" "bug manifests in the released version without warning. The deadline for a\n" "certain feature is moved closer. A developer whose help you need for a key section of the project is about to leave. In all cases, you must abruptly drop what you are doing and focus on a completely different task.\n" msgstr "*Problem*: Externe Faktoren zwingen zum Wechsel des Kontext. Ein schwerwiegender Fehler in der veröffentlichten Version tritt ohne Vorwarnung auf. Die Frist für ein bestimmtes Leistungsmerkmal rückt näher. Ein Entwickler, dessen Unterstützung für eine Schlüsselstelle im Projekt wichtig ist, verlässt das Team. In allen Fällen musst du alles stehen und liegen lassen und dich auf eine komplett andere Aufgabe konzentrieren.\n" #. type: Plain text #: ../en/branch.txt:10 msgid "" "Interrupting your train of thought can be detrimental to your productivity, " "and the more cumbersome it is to switch contexts, the greater the loss. With " "centralized version control we must download a fresh working copy from the " "central server. Distributed systems fare better, as we can clone the desired " "version locally." msgstr "" "Den Gedankengang zu unterbrechen ist schlecht für die Produktivität und je " "komplizierter der Kontextwechsel ist, desto größer ist der Verlust. Mit " "zentraler Versionsverwaltung müssen wir eine neue Arbeitskopie vom Server " "herunterladen. Bei verteilen Systemen ist das viel besser, da wir die " "benötigt Version lokal 'clonen' können." #. type: Plain text #: ../en/branch.txt:12 msgid "" "But cloning still entails copying the whole working directory as well as the " "entire history up to the given point. Even though Git reduces the cost of " "this with file sharing and hard links, the project files themselves must be " "recreated in their entirety in the new working directory." msgstr "" "Doch das 'Clonen' bringt das Kopieren des gesamten Arbeitsverzeichnis wie " "auch die ganze Geschichte bis zum angegebenen Punkt mit sich. Auch wenn Git " "die Kosten durch Dateifreigaben und Verknüpfungen reduziert, müssen doch die " "gesamten Projektdateien im neuen Arbeitsverzeichnis erstellt werden." #. type: Plain text #: ../en/branch.txt:14 #, no-wrap msgid "*Solution*: Git has a better tool for these situations that is much faster and more space-efficient than cloning: *git branch*.\n" msgstr "*Lösung*: Git hat ein besseres Werkzeug für diese Situationen, die wesentlich schneller und platzsparender als 'clonen' ist: *git branch*.\n" #. type: Plain text #: ../en/branch.txt:16 msgid "" "With this magic word, the files in your directory suddenly shapeshift from " "one version to another. This transformation can do more than merely go back " "or forward in history. Your files can morph from the last release to the " "experimental version to the current development version to your friend's " "version and so on." msgstr "" "Mit diesem Zauberwort verwandeln sich die Dateien in deinem " "Arbeitsverzeichnis plötzlich von einer Version in eine andere. Diese " "Verwandlung kann mehr als nur in der Geschichte vor und zurück gehen. Deine " "Dateien können sich verwandeln, vom aktuellsten Stand, zur experimentellen " "Version, zum neusten Entwicklungsstand, zur Version deines Freundes und so " "weiter." #. type: Plain text #: ../en/branch.txt:18 msgid "=== The Boss Key ===" msgstr "=== Die Chef-Taste ===" #. type: Plain text #: ../en/branch.txt:20 msgid "" "Ever played one of those games where at the push of a button (``the boss " "key''), the screen would instantly display a spreadsheet or something? So if " "the boss walked in the office while you were playing the game you could " "quickly hide it away?" msgstr "" "Hast du schon einmal ein Spiel gespielt, wo beim Drücken einer Taste (``der " "Chef-Taste''), der Monitor sofort ein Tabellenblatt oder etwas anderes " "angezeigt hat? Dass, wenn der Chef ins Büro spaziert, während du das Spiel " "spielst, du es schnell verstecken kannst?" #. type: Plain text #: ../en/branch.txt:22 msgid "In some directory:" msgstr "In irgendeinem Verzeichnis:" #. type: Plain text #: ../en/branch.txt:27 #, no-wrap msgid "" " $ echo \"I'm smarter than my boss\" > myfile.txt\n" " $ git init\n" " $ git add .\n" " $ git commit -m \"Initial commit\"\n" msgstr "" " $ echo \"Ich bin klüger als mein Chef\" > meinedatei.txt\n" " $ git init\n" " $ git add .\n" " $ git commit -m \"Erster Stand\"\n" #. type: Plain text #: ../en/branch.txt:29 msgid "" "We have created a Git repository that tracks one text file containing a " "certain message. Now type:" msgstr "" "Wir haben ein Git 'Repository' erstellt, das eine Textdatei mit einer " "bestimmten Nachricht enthält. Nun gib ein:" #. type: Plain text #: ../en/branch.txt:33 #, no-wrap msgid "" " $ git checkout -b boss # nothing seems to change after this\n" " $ echo \"My boss is smarter than me\" > myfile.txt\n" " $ git commit -a -m \"Another commit\"\n" msgstr "" " $ git checkout -b chef # scheinbar hat sich danach nichts geändert\n" " $ echo \"Mein Chef ist klüger als ich\" > meinedatei.txt\n" " $ git commit -a -m \"Ein anderer Stand\"\n" #. type: Plain text #: ../en/branch.txt:35 msgid "" "It looks like we've just overwritten our file and committed it. But it's an " "illusion. Type:" msgstr "" "Es sieht aus als hätten wir unsere Datei überschrieben und 'commitet'. Aber " "es ist eine Illusion. Tippe:" #. type: Plain text #: ../en/branch.txt:37 #, no-wrap msgid " $ git checkout master # switch to original version of the file\n" msgstr " $ git checkout master # wechsle zur Originalversion der Datei\n" #. type: Plain text #: ../en/branch.txt:39 msgid "" "and hey presto! The text file is restored. And if the boss decides to snoop " "around this directory, type:" msgstr "" "und Simsalabim! Die Textdatei ist wiederhergestellt. Und wenn der Chef in " "diesem Verzeichnis herumschnüffelt, tippe:" #. type: Plain text #: ../en/branch.txt:41 #, no-wrap msgid " $ git checkout boss # switch to version suitable for boss' eyes\n" msgstr " $ git checkout chef # wechsle zur Version die der Chef ruhig sehen kann\n" #. type: Plain text #: ../en/branch.txt:43 msgid "" "You can switch between the two versions of the file as much as you like, and " "commit to each independently." msgstr "" "Du kannst zwischen den beiden Versionen wechseln, so oft du willst und du " "kannst unabhängig voneinander in jeder Version Änderungen 'commiten'" #. type: Plain text #: ../en/branch.txt:45 msgid "=== Dirty Work ===" msgstr "=== Schmutzarbeit ===" #. type: Plain text #: ../en/branch.txt:48 msgid "" "[[branch]] Say you're working on some feature, and for some reason, you need " "to go back three versions and temporarily put in a few print statements to " "see how something works. Then:" msgstr "" "[[branch]] Sagen wir, du arbeitest an einer Funktion und du musst, warum " "auch immer, drei Versionen zurückgehen um ein paar print Anweisungen " "einzufügen, damit du siehst, wie etwas funktioniert. Dann:" #. type: Plain text #: ../en/branch.txt:51 #, no-wrap msgid "" " $ git commit -a\n" " $ git checkout HEAD~3\n" msgstr "" " $ git commit -a\n" " $ git checkout HEAD~3\n" #. type: Plain text #: ../en/branch.txt:53 msgid "" "Now you can add ugly temporary code all over the place. You can even commit " "these changes. When you're done," msgstr "" "Nun kannst du überall wild temporären Code hinzufügen. Du kannst diese " "Änderungen sogar 'commiten'. Wenn du fertig bist," #. type: Plain text #: ../en/branch.txt:55 #, no-wrap msgid " $ git checkout master\n" msgstr " $ git checkout master\n" #. type: Plain text #: ../en/branch.txt:57 msgid "" "to return to your original work. Observe that any uncommitted changes are " "carried over." msgstr "" "um zur ursprünglichen Arbeit zurückzukehren. Beachte, dass alle Änderungen, " "die nicht 'commitet' sind übernommen werden." #. type: Plain text #: ../en/branch.txt:59 msgid "What if you wanted to save the temporary changes after all? Easy:" msgstr "" "Was, wenn du am Ende die temporären Änderungen sichern willst? Einfach:" #. type: Plain text #: ../en/branch.txt:61 #, no-wrap msgid " $ git checkout -b dirty\n" msgstr " $ git checkout -b schmutzig\n" #. type: Plain text #: ../en/branch.txt:63 msgid "" "and commit before switching back to the master branch. Whenever you want to " "return to the dirty changes, simply type:" msgstr "" "und 'commite' bevor du auf den 'Master Branch' zurückschaltest. Wann immer " "du zu deiner Schmutzarbeit zurückkehren willst, tippe einfach:" #. type: Plain text #: ../en/branch.txt:65 #, no-wrap msgid " $ git checkout dirty\n" msgstr " $ git checkout schnmutzig\n" #. type: Plain text #: ../en/branch.txt:67 msgid "" "We touched upon this command in an earlier chapter, when discussing loading " "old states. At last we can tell the whole story: the files change to the " "requested state, but we must leave the master branch. Any commits made from " "now on take your files down a different road, which can be named later." msgstr "" "Wir sind mit dieser Anweisung schon in einem früheren Kapitel in Berührung " "gekommen, als wir das Laden alter Stände besprochen haben. Nun können wir " "die ganze Geschichte erzählen: Die Dateien ändern sich zu dem angeforderten " "Stand, aber wir müssen den 'Master Branch' verlassen. Jeder 'Commit' ab " "jetzt führt deine Dateien auf einen anderen Weg, dem wir später noch einen " "Namen geben können." #. type: Plain text #: ../en/branch.txt:69 msgid "" "In other words, after checking out an old state, Git automatically puts you " "in a new, unnamed branch, which can be named and saved with *git checkout -" "b*." msgstr "" "Mit anderen Worten, nach dem Abrufen eines alten Stands versetzt dich Git " "automatisch in einen neuen, unbenannten 'Branch', der mit *git checkout -b* " "benannt und gesichert werden kann." #. type: Plain text #: ../en/branch.txt:71 msgid "=== Quick Fixes ===" msgstr "=== Schnelle Fehlerbehebung ===" #. type: Plain text #: ../en/branch.txt:73 msgid "" "You're in the middle of something when you are told to drop everything and " "fix a newly discovered bug in commit `1b6d...`:" msgstr "" "Du steckst mitten in der Arbeit, als es heißt alles fallen zu lassen um " "einen neu entdeckten Fehler in 'Commit' `1b6d...` zu beheben:" #. type: Plain text #: ../en/branch.txt:76 #, no-wrap msgid "" " $ git commit -a\n" " $ git checkout -b fixes 1b6d\n" msgstr "" " $ git commit -a\n" " $ git checkout -b fixes 1b6d\n" #. type: Plain text #: ../en/branch.txt:78 msgid "Then once you've fixed the bug:" msgstr "Dann, wenn du den Fehler behoben hast:" #. type: Plain text #: ../en/branch.txt:81 #, no-wrap msgid "" " $ git commit -a -m \"Bug fixed\"\n" " $ git checkout master\n" msgstr "" " $ git commit -a -m \"Fehler behoben\"\n" " $ git checkout master\n" #. type: Plain text #: ../en/branch.txt:84 msgid "" "and resume work on your original task. You can even 'merge' in the freshly " "baked bugfix:" msgstr "" "und fahre mit deiner ursprünglichen Arbeit fort. Du kannst sogar die frisch " "gebackene Fehlerkorrektur auf Deinen aktuellen Stand übernehmen:" #. type: Plain text #: ../en/branch.txt:86 #, no-wrap msgid " $ git merge fixes\n" msgstr " $ git merge fixes\n" #. type: Plain text #: ../en/branch.txt:88 msgid "=== Merging ===" msgstr "=== 'Mergen' ===" #. type: Plain text #: ../en/branch.txt:92 msgid "" "With some version control systems, creating branches is easy but merging " "them back together is tough. With Git, merging is so trivial that you might " "be unaware of it happening." msgstr "" "Mit einigen Versionsverwaltungssystemen ist das Erstellen eines 'Branch' " "einfach, aber das Zusammenfügen ('Mergen') ist schwierig. Mit Git ist " "'Mergen' so einfach, dass du gar nicht merkst, wenn es passiert." #. type: Plain text #: ../en/branch.txt:98 msgid "" "We actually encountered merging long ago. The *pull* command in fact " "'fetches' commits and then merges them into your current branch. If you have " "no local changes, then the merge is a 'fast forward', a degenerate case akin " "to fetching the latest version in a centralized version control system. But " "if you do have local changes, Git will automatically merge, and report any " "conflicts." msgstr "" "Tatsächlich sind wir dem 'Mergen' schon lange begegnet. Die *pull* Anweisung " "holt ('fetch') eigentlich die 'Commits' und verschmilzt ('merged') diese " "dann mit dem aktuellen 'Branch'. Wenn du keine lokalen Änderungen hast, dann " "ist 'merge' eine 'schnelle Weiterleitung', ein Ausnahmefall, ähnlich dem " "Abrufen der letzten Version eines zentralen Versionsverwaltungssystems. Wenn " "du aber Änderungen hast, wird Git diese automatisch 'mergen' und dir " "Konflikte melden." #. type: Plain text #: ../en/branch.txt:103 msgid "" "Ordinarily, a commit has exactly one 'parent commit', namely, the previous " "commit. Merging branches together produces a commit with at least two " "parents. This begs the question: what commit does `HEAD~10` really refer " "to? A commit could have multiple parents, so which one do we follow?" msgstr "" "Normalerweise hat ein 'Commit' genau einen Eltern-'Commit', nämlich den " "vorhergehenden 'Commit'. Das 'Mergen' mehrerer 'Branches' erzeugt einen " "'Commit' mit mindestens zwei Eltern. Das wirft die Frage auf: Welchen " "'Commit' referenziert `HEAD~10` tatsächlich? Ein 'Commit' kann mehrere " "Eltern haben, welchem folgen wir also?" #. type: Plain text #: ../en/branch.txt:108 msgid "" "It turns out this notation chooses the first parent every time. This is " "desirable because the current branch becomes the first parent during a " "merge; frequently you're only concerned with the changes you made in the " "current branch, as opposed to changes merged in from other branches." msgstr "" "Es stellt sich heraus, dass diese Notation immer den ersten Elternteil " "wählt. Dies ist erstrebenswert, denn der aktuelle 'Branch' wird zum ersten " "Elternteil während eines 'Merge'; häufig bist du nur von Änderungen " "betroffen, die du im aktuellen 'Branch' gemacht hast, als von den Änderungen " "die von anderen 'Branches' eingebracht wurden." #. type: Plain text #: ../en/branch.txt:111 msgid "" "You can refer to a specific parent with a caret. For example, to show the " "logs from the second parent:" msgstr "" "Du kannst einen bestimmten Elternteil mit einem Caret-Zeichen referenzieren. " "Um zum Beispiel die Logs vom zweiten Elternteil anzuzeigen:" #. type: Plain text #: ../en/branch.txt:113 #, no-wrap msgid " $ git log HEAD^2\n" msgstr " $ git log HEAD^2\n" #. type: Plain text #: ../en/branch.txt:116 msgid "" "You may omit the number for the first parent. For example, to show the " "differences with the first parent:" msgstr "" "Du kannst die Nummer für den ersten Elternteil weglassen. Um zum Beispiel " "die Unterschiede zum ersten Elternteil anzuzeigen:" #. type: Plain text #: ../en/branch.txt:118 #, no-wrap msgid " $ git diff HEAD^\n" msgstr " $ git diff HEAD^\n" #. type: Plain text #: ../en/branch.txt:120 msgid "You can combine this notation with other types. For example:" msgstr "Du kannst diese Notation mit anderen Typen kombinieren. Zum Beispiel:" #. type: Plain text #: ../en/branch.txt:122 #, no-wrap msgid " $ git checkout 1b6d^^2~10 -b ancient\n" msgstr " $ git checkout 1b6d^^2~10 -b uralt\n" #. type: Plain text #: ../en/branch.txt:125 msgid "" "starts a new branch ``ancient'' representing the state 10 commits back from " "the second parent of the first parent of the commit starting with 1b6d." msgstr "" "beginnt einen neuen 'Branch' ``uralt'', welcher den Stand 10 'Commits' " "zurück vom zweiten Elternteil des ersten Elternteil des 'Commits', dessen " "Hashwert mit 1b6d beginnt." #. type: Plain text #: ../en/branch.txt:127 msgid "=== Uninterrupted Workflow ===" msgstr "=== Kontinuierlicher Arbeitsfluss ===" #. type: Plain text #: ../en/branch.txt:129 msgid "" "Often in hardware projects, the second step of a plan must await the " "completion of the first step. A car undergoing repairs might sit idly in a " "garage until a particular part arrives from the factory. A prototype might " "wait for a chip to be fabricated before construction can continue." msgstr "" "In Herstellungsprozessen muss der zweiter Schritt eines Plans oft auf die " "Fertigstellung des ersten Schritt warten. Ein Auto, das repariert werden " "soll, steht unbenutzt in der Garage bis ein Ersatzteil geliefert wird. Ein " "Prototyp muss warten, bis ein Baustein fabriziert wurde, bevor die " "Konstruktion fortgesetzt werden kann." #. type: Plain text #: ../en/branch.txt:134 msgid "" "Software projects can be similar. The second part of a new feature may have " "to wait until the first part has been released and tested. Some projects " "require your code to be reviewed before accepting it, so you might wait " "until the first part is approved before starting the second part." msgstr "" "Bei Softwareprojekten kann das ähnlich sein. Der zweite Teil eines " "Leistungsmerkmals muss warten, bis der erste Teil veröffentlicht und " "getestet wurde. Einige Projekte erfordern, dass dein Code überprüft werden " "muss bevor er akzeptiert wird, du musst also warten, bis der erste Teil " "geprüft wurde, bevor du mit dem zweiten Teil anfangen kannst." #. type: Plain text #: ../en/branch.txt:139 msgid "" "Thanks to painless branching and merging, we can bend the rules and work on " "Part II before Part I is officially ready. Suppose you have committed Part I " "and sent it for review. Let's say you're in the `master` branch. Then branch " "off:" msgstr "" "Dank des schmerzlosen 'Branchen' und 'Mergen' können wir die Regeln beugen " "und am Teil II arbeiten, bevor Teil I offiziell freigegeben wurde. " "Angenommen du hast Teil I 'commitet' und zur Prüfung eingereicht. Sagen wir " "du bist im `master` 'Branch'. Dann 'branche' zu Teil II:" #. type: Plain text #: ../en/branch.txt:141 #, no-wrap msgid " $ git checkout -b part2\n" msgstr " $ git checkout -b teil2\n" #. type: Plain text #: ../en/branch.txt:145 msgid "" "Next, work on Part II, committing your changes along the way. To err is " "human, and often you'll want to go back and fix something in Part I. If " "you're lucky, or very good, you can skip these lines." msgstr "" "Du arbeitest also an Teil II und 'commitest' deine Änderungen regelmäßig. " "Irren ist menschlich und so kann es vorkommen, dass du zurück zu Teil I " "willst um einen Fehler zu beheben. Wenn du Glück hast oder sehr gut bist, " "kannst du die nächsten Zeilen überspringen." #. type: Plain text #: ../en/branch.txt:151 #, no-wrap msgid "" " $ git checkout master # Go back to Part I.\n" " $ fix_problem\n" " $ git commit -a # Commit the fixes.\n" " $ git checkout part2 # Go back to Part II.\n" " $ git merge master # Merge in those fixes.\n" msgstr "" " $ git checkout master # Gehe zurück zu Teil I.\n" " $ fix_problem\n" " $ git commit -a # 'Commite' die Lösung.\n" " $ git checkout teil2 # Gehe zurück zu Teil II.\n" " $ git merge master # 'Merge' die Lösung.\n" #. type: Plain text #: ../en/branch.txt:153 msgid "Eventually, Part I is approved:" msgstr "Schließlich, Teil I ist zugelassen:" #. type: Plain text #: ../en/branch.txt:158 #, no-wrap msgid "" " $ git checkout master # Go back to Part I.\n" " $ submit files # Release to the world!\n" " $ git merge part2 # Merge in Part II.\n" " $ git branch -d part2 # Delete \"part2\" branch.\n" msgstr "" " $ git checkout master # Gehe zurück zu Teil I.\n" " $ submit files # Veröffentliche deine Dateien!\n" " $ git merge teil2 # 'Merge' in Teil II.\n" " $ git branch -d teil2 # Lösche den Branch \"teil2\"\n" #. type: Plain text #: ../en/branch.txt:160 msgid "" "Now you're in the `master` branch again, with Part II in the working " "directory." msgstr "" "Nun bist du wieder im `master` 'Branch', mit Teil II im Arbeitsverzeichnis." #. type: Plain text #: ../en/branch.txt:164 msgid "" "It's easy to extend this trick for any number of parts. It's also easy to " "branch off retroactively: suppose you belatedly realize you should have " "created a branch 7 commits ago. Then type:" msgstr "" "Es ist einfach, diesen Trick auf eine beliebige Anzahl von Teilen zu " "erweitern. Es ist genauso einfach rückwirkend zu 'branchen': angenommen, du " "merkst zu spät, dass vor sieben 'Commits' ein 'Branch' erforderlich gewesen " "wäre. Dann tippe:" #. type: Plain text #: ../en/branch.txt:167 #, no-wrap msgid "" " $ git branch -m master part2 # Rename \"master\" branch to \"part2\".\n" " $ git branch master HEAD~7 # Create new \"master\", 7 commits upstream.\n" msgstr "" " $ git branch -m master teil2 # Umbenennen des 'Branch' \"master\" zu \"teil2\".\n" " $ git branch master HEAD~7 # Erstelle neuen \"master\", 7 Commits voraus\n" #. type: Plain text #: ../en/branch.txt:172 msgid "" "The `master` branch now contains just Part I, and the `part2` branch " "contains the rest. We are in the latter branch; we created `master` without " "switching to it, because we want to continue work on `part2`. This is " "unusual. Until now, we've been switching to branches immediately after " "creation, as in:" msgstr "" "Der `master` Branch enthält nun Teil I, und der `teil2` Branch enthält den " "Rest. Wir befinden uns in letzterem Branch; wir haben `master` erzeugt ohne " "dorthin zu wechseln, denn wir wollen im `teil2` weiterarbeiten. Das ist " "unüblich. Bisher haben wir unmittelbar nach dem Erstellen in einen 'Branch' " "gewechselt, wie in:" #. type: Plain text #: ../en/branch.txt:174 #, no-wrap msgid " $ git checkout HEAD~7 -b master # Create a branch, and switch to it.\n" msgstr " $ git checkout HEAD~7 -b master # erzeuge einen Branch, und wechsle zu ihm.\n" #. type: Plain text #: ../en/branch.txt:176 msgid "=== Reorganizing a Medley ===" msgstr "=== Mischmasch Reorganisieren ===" #. type: Plain text #: ../en/branch.txt:178 msgid "" "Perhaps you like to work on all aspects of a project in the same branch. You " "want to keep works-in-progress to yourself and want others to see your " "commits only when they have been neatly organized. Start a couple of " "branches:" msgstr "" "Vielleicht magst du es, alle Aspekte eines Projekts im selben 'Branch' " "abzuarbeiten. Du willst deine laufenden Arbeiten für dich behalten und " "andere sollen deine 'Commits' nur sehen, wenn du sie hübsch organisiert " "hast. Beginne ein paar 'Branches':" #. type: Plain text #: ../en/branch.txt:181 #, no-wrap msgid "" " $ git branch sanitized # Create a branch for sanitized commits.\n" " $ git checkout -b medley # Create and switch to a branch to work in.\n" msgstr "" " $ git branch sauber # Erzeuge einen Branch für gesäuberte Commits.\n" " $ git checkout -b mischmasch # Erzeuge und wechsle in den Branch zum Arbeiten.\n" #. type: Plain text #: ../en/branch.txt:183 msgid "" "Next, work on anything: fix bugs, add features, add temporary code, and so " "forth, committing often along the way. Then:" msgstr "" "Fahre fort alles zu bearbeiten: Behebe Fehler, füge Funktionen hinzu, " "erstelle temporären Code und so weiter und 'commite' deine Änderungen oft. " "Dann:" #. type: Plain text #: ../en/branch.txt:186 #, no-wrap msgid "" " $ git checkout sanitized\n" " $ git cherry-pick medley^^\n" msgstr "" " $ git checkout bereinigt\n" " $ git cherry-pick mischmasch^^\n" #. type: Plain text #: ../en/branch.txt:188 msgid "" "applies the grandparent of the head commit of the ``medley'' branch to the " "``sanitized'' branch. With appropriate cherry-picks you can construct a " "branch that contains only permanent code, and has related commits grouped " "together." msgstr "" "wendet den Urahn des obersten 'Commit' des ``mischmasch'' 'Branch' auf den " "``bereinigt'' 'Branch' an. Durch das Herauspicken der Rosinen kannst du " "einen 'Branch' konstruieren, der nur endgültigen Code enthält und " "zusammengehörige 'Commits' gruppiert hat." #. type: Plain text #: ../en/branch.txt:190 msgid "=== Managing Branches ===" msgstr "=== 'Branches' verwalten ===" #. type: Plain text #: ../en/branch.txt:192 msgid "List all branches by typing:" msgstr "Ein Liste aller 'Branches' bekommst du mit:" #. type: Plain text #: ../en/branch.txt:194 #, no-wrap msgid " $ git branch\n" msgstr " $ git branch\n" #. type: Plain text #: ../en/branch.txt:197 msgid "" "By default, you start in a branch named ``master''. Some advocate leaving " "the ``master'' branch untouched and creating new branches for your own edits." msgstr "" "Standardmäßig beginnst du in einem 'Branch' namens ``master''. Einige " "plädieren dafür, den ``master'' 'Branch' unangetastet zu lassen und für " "seine Arbeit einen neuen 'Branch' anzulegen." #. type: Plain text #: ../en/branch.txt:200 msgid "" "The *-d* and *-m* options allow you to delete and move (rename) branches. " "See *git help branch*." msgstr "" "Die *-d* und *-m* Optionen erlauben dir 'Branches' zu löschen und zu " "verschieben (umzubenennen). Siehe *git help branch*." #. type: Plain text #: ../en/branch.txt:205 msgid "" "The ``master'' branch is a useful custom. Others may assume that your " "repository has a branch with this name, and that it contains the official " "version of your project. Although you can rename or obliterate the " "``master'' branch, you might as well respect this convention." msgstr "" "Der ``master'' 'Branch' ist ein nützlicher Brauch. Andere können davon " "ausgehen, dass dein 'Repository' einen 'Branch' mit diesem Namen hat und " "dass er die offizielle Version enthält. Auch wenn du den ``master'' 'Branch' " "umbenennen oder auslöschen könntest, kannst du diese Konvention aber auch " "respektieren." #. type: Plain text #: ../en/branch.txt:207 msgid "=== Temporary Branches ===" msgstr "=== Temporäre 'Branches' ===" #. type: Plain text #: ../en/branch.txt:212 msgid "" "After a while you may realize you are creating short-lived branches " "frequently for similar reasons: every other branch merely serves to save the " "current state so you can briefly hop back to an older state to fix a high-" "priority bug or something." msgstr "" "Nach einer Weile wirst du feststellen, dass du regelmäßig kurzlebige " "'Branches' erzeugst, meist aus dem gleichen Grund: jeder neue 'Branch' dient " "lediglich dazu, den aktuellen Stand zu sichern, damit du kurz zu einem alten " "Stand zurück kannst um eine vorrangige Fehlerbehebung zu machen oder " "irgendetwas anderes." #. type: Plain text #: ../en/branch.txt:217 msgid "" "It's analogous to changing the TV channel temporarily to see what else is " "on. But instead of pushing a couple of buttons, you have to create, check " "out, merge, and delete temporary branches. Luckily, Git has a shortcut that " "is as convenient as a TV remote control:" msgstr "" "Es ist vergleichbar mit dem kurzzeitigen Umschalten des Fernsehkanals um zu " "sehen was auf dem anderen Kanal los ist. Doch anstelle ein paar Knöpfe zu " "drücken, machst du 'create', 'check out', 'merge' und 'delete' von " "temporären 'Branches'. Glücklicherweise hat Git eine Abkürzung dafür, die " "genauso komfortabel ist wie eine Fernbedienung:" #. type: Plain text #: ../en/branch.txt:219 #, no-wrap msgid " $ git stash\n" msgstr " $ git stash\n" #. type: Plain text #: ../en/branch.txt:224 msgid "" "This saves the current state in a temporary location (a 'stash') and " "restores the previous state. Your working directory appears exactly as it " "was before you started editing, and you can fix bugs, pull in upstream " "changes, and so on. When you want to go back to the stashed state, type:" msgstr "" "Das sichert den aktuellen Stand an einem temporären Ort ('stash'=Versteck) " "und stellt den vorherigen Stand wieder her. Dein Arbeitsverzeichnis " "erscheint wieder exakt in dem Zustand wie es war, bevor du anfingst zu " "editieren. Nun kannst du Fehler beheben, Änderungen vom zentralen " "'Repository' holen ('pull') und so weiter. Wenn du wieder zurück zu deinen " "Änderungen willst, tippe:" #. type: Plain text #: ../en/branch.txt:226 #, no-wrap msgid " $ git stash apply # You may need to resolve some conflicts.\n" msgstr " $ git stash apply # Es kann sein, dass du Konflikte auflösen musst.\n" #. type: Plain text #: ../en/branch.txt:229 msgid "" "You can have multiple stashes, and manipulate them in various ways. See *git " "help stash*. As you may have guessed, Git maintains branches behind the " "scenes to perform this magic trick." msgstr "" "Du kannst mehrere 'stashes' haben und diese unterschiedlich handhaben. Siehe " "*git help stash*. Wie du dir vielleicht schon gedacht hast, verwendet Git " "'Branches' im Hintergrund um diesen Zaubertrick durchzuführen." #. type: Plain text #: ../en/branch.txt:231 msgid "=== Work How You Want ===" msgstr "=== Arbeite wie du willst ===" #. type: Plain text #: ../en/branch.txt:235 msgid "" "You might wonder if branches are worth the bother. After all, clones are " "almost as fast, and you can switch between them with *cd* instead of " "esoteric Git commands." msgstr "" "Du magst dich fragen, ob 'Branches' diesen Aufwand Wert sind. Immerhin sind " "'Clone' fast genauso schnell und du kannst mit *cd* anstelle von " "esoterischen Git Befehlen zwischen ihnen wechseln." #. type: Plain text #: ../en/branch.txt:241 msgid "" "Consider web browsers. Why support multiple tabs as well as multiple " "windows? Because allowing both accommodates a wide variety of styles. Some " "users like to keep only one browser window open, and use tabs for multiple " "webpages. Others might insist on the other extreme: multiple windows with no " "tabs anywhere. Others still prefer something in between." msgstr "" "Betrachten wir Webbrowser. Warum mehrere Tabs unterstützen und mehrere " "Fenster? Weil beides zu erlauben eine Vielzahl an Stilen unterstützt. Einige " "Anwender möchten nur ein Browserfenster geöffnet haben und benutzen Tabs für " "unterschiedliche Webseiten. Andere bestehen auf dem anderen Extrem: mehrere " "Fenster, ganz ohne Tabs. Wieder andere bevorzugen irgendetwas dazwischen." #. type: Plain text #: ../en/branch.txt:245 msgid "" "Branching is like tabs for your working directory, and cloning is like " "opening a new browser window. These operations are fast and local, so why " "not experiment to find the combination that best suits you? Git lets you " "work exactly how you want." msgstr "" "'Branchen' ist wie Tabs für dein Arbeitsverzeichnis und 'Clonen' ist wie das " "Öffnen eines neuen Browserfenster. Diese Operationen sind schnell und lokal, " "also warum nicht damit experimentieren um die beste Kombination für sich " "selbst zu finden? Git lässt dich genauso arbeiten, wie du es willst." #~ msgid "You can even 'merge' in the bugfix you just made, either by typing:" #~ msgstr "" #~ "Du kannst die Fehlerbehebung, die du gerade gemacht hast, auch 'mergen'. " #~ "Entweder durch:" #~ msgid "or:" #~ msgstr "oder:" #~ msgid " $ git pull\n" #~ msgstr " $ git pull\n" #~ msgid "since you have already pushed the bugfix to the main repository." #~ msgstr "" #~ "da du die Fehlerbehebung schon ins zentrale 'Repository' ge'pushed' hast." #~ msgid "" #~ "The `master` branch now contains just Part I, and the `part2` branch " #~ "contains the rest." #~ msgstr "" #~ "Der `master` 'Branch' enthält nun nur den Teil I und der `teil2` 'Branch' " #~ "enthält den Rest." #~ msgid "" #~ " $ git checkout -b sanitized\n" #~ " $ git checkout -b medley\n" #~ msgstr "" #~ " $ git checkout -b bereinigt\n" #~ " $ git checkout -b mischmasch\n" gitmagic-20160304/de/pot/translate.po0000644000175000017500000001156012666307504016532 0ustar sbadiasbadia# Git Magic - A guide to using Git # This file is distributed under the GNU GENERAL PUBLIC LICENSE Version 3. # Benn Lynn , 2007. # Armin Stebich , 2010, 2011. # msgid "" msgstr "" "Project-Id-Version: Git Magic deutsch\n" "Report-Msgid-Bugs-To: bennlynn@gmail.com\n" "POT-Creation-Date: 2011-07-03 23:06+0300\n" "PO-Revision-Date: 2011-07-07 18:41+0200\n" "Last-Translator: Armin Stebich \n" "Language-Team: DE \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: UTF-8\n" "Plural-Forms: \n" #. type: Plain text #: ../en/translate.txt:2 msgid "== Appendix B: Translating This Guide ==" msgstr "== Anhang B: Diese Anleitung übersetzen ==" #. type: Plain text #: ../en/translate.txt:6 msgid "" "I recommend the following steps for translating this guide, so my scripts " "can quickly produce HTML and PDF versions, and all translations can live in " "the same repository." msgstr "" "Ich empfehle folgende Schritte um diese Anleitung zu übersetzen, damit meine " "Skripte einfach eine HTML- und PDF-Version erstellen können. Außerdem können " "so alle Übersetzungen in einem 'Repository' existieren." #. type: Plain text #: ../en/translate.txt:13 msgid "" "Clone the source, then create a directory corresponding to the target " "language's IETF tag: see http://www.w3.org/International/articles/language-" "tags/Overview.en.php[the W3C article on internationalization]. For example, " "English is \"en\" and Japanese is \"ja\". In the new directory, and " "translate the +txt+ files from the \"en\" subdirectory." msgstr "" "'Clone' die Quelltexte, dann erstelle ein Verzeichnis mit dem Namen des IETF " "Sprachkürzel der übersetzten Sprache: siehe http://www.w3.org/International/" "articles/language-tags/Overview.en.php[den W3C Artikel über " "Internationalisierung]. Zum Beispiel, Englisch ist \"en\", Japanisch ist \"ja" "\". Kopiere alle +txt+-Dateien aus dem \"en\"-Verzeichnis in das neue " "Verzeichnis und übersetze diese." #. type: Plain text #: ../en/translate.txt:15 msgid "" "For instance, to translate the guide into http://en.wikipedia.org/wiki/" "Klingon_language[Klingon], you might type:" msgstr "" "Um zum Beispiel die Anleitung auf http://de.wikipedia.org/wiki/" "Klingonische_Sprache[Klingonisch] zu übersetzen, mußt du folgendes machen:" #. type: Plain text #: ../en/translate.txt:22 #, no-wrap msgid "" " $ git clone git://repo.or.cz/gitmagic.git\n" " $ cd gitmagic\n" " $ mkdir tlh # \"tlh\" is the IETF language code for Klingon.\n" " $ cd tlh\n" " $ cp ../en/intro.txt .\n" " $ edit intro.txt # Translate the file.\n" msgstr "" " $ git clone git://repo.or.cz/gitmagic.git\n" " $ cd gitmagic\n" " $ mkdir tlh # \"tlh\" ist das IETF Sprachkürzel für Klingonisch.\n" " $ cd tlh\n" " $ cp ../en/intro.txt .\n" " $ edit intro.txt # übersetze diese Datei.\n" #. type: Plain text #: ../en/translate.txt:24 msgid "and so on for each text file." msgstr "und das machst du für jede txt-Datei." #. type: Plain text #: ../en/translate.txt:27 msgid "" "Edit the Makefile and add the language code to the `TRANSLATIONS` variable. " "You can now review your work incrementally:" msgstr "" "Bearbeite das Makefile und füge das Sprachkürzel zur Variable `TRANSLATIONS` " "hinzu. Nun kannst Du Deine Arbeit jederzeit wie folgt überprüfen:" #. type: Plain text #: ../en/translate.txt:30 #, no-wrap msgid "" " $ make tlh\n" " $ firefox book-tlh/index.html\n" msgstr "" " $ make tlh\n" " $ firefox book-tlh/index.html\n" #. type: Plain text #: ../en/translate.txt:33 msgid "" "Commit your changes often, then let me know when they're ready. GitHub has " "an interface that facilitates this: fork the \"gitmagic\" project, push your " "changes, then ask me to merge." msgstr "" "'Committe' deine Änderungen oft und wenn du fertig bist, gib bitte Bescheid. " "GitHub hat eine Schnittstelle, die das erleichtert: Erzeuge deine eigene " "'Fork' vom \"gitmagic\" Projekt, 'pushe' deine Änderungen, dann gib mir " "Bescheid, deine Änderungen zu 'mergen'." #~ msgid "" #~ "I like to have translations follow the above scheme so my scripts can " #~ "produce HTML and PDF versions. Also, it conveniently keeps all the " #~ "translations in the official repository. But please do whatever suits you " #~ "best: for example, the Chinese translators used Google Docs. I'm happy as " #~ "long as your work enables more people to access my work." #~ msgstr "" #~ "Ich bevorzuge Übersetzungen nach diesem Schema, denn so können meine " #~ "Skripte HTML und PDF Versionen erstellen. Außerdem hält es alle " #~ "Übersetzungen bequem im offiziellen 'Repository'. Trotzdem, wähle den " #~ "Weg, der dir am besten liegt. Die chinesischen Übersetzer zum Beispiel " #~ "benutzen Google Docs. Ich bin glücklich, so lange deine Arbeit mehr " #~ "Menschen den Zugang zu meiner Arbeit ermöglicht." gitmagic-20160304/de/grandmaster.txt0000644000175000017500000002770312666307504016451 0ustar sbadiasbadia== Git für Fortgeschrittene == Mittlerweile solltest Du Dich in den *git help* Seiten zurechtfinden und das meiste verstanden haben. Trotzdem kann es langwierig sein, den exakten Befehl zur Lösung einer bestimmten Aufgabe herauszufinden. Vielleicht kann ich Dir etwas Zeit sparen: Nachfolgend findest Du ein paar Rezepte, die ich in der Vergangenheit gebraucht habe. === Quellcode veröffentlichen === Bei meinen Projekten verwaltet Git genau die Dateien, die ich archivieren und für andere Benutzer veröffentlichen will. Um ein tarball-Archiv des Quellcodes zu erzeugen, verwende ich den Befehl: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === 'Commite' Änderungen === Git mitzuteilen, welche Dateien man hinzugefügt, gelöscht und umbenannt hat, ist für manche Projekte sehr mühsam. Stattdessen kann man folgendes eingeben: $ git add . $ git add -u Git wird sich die Dateien im aktuellen Verzeichnis ansehen und sich die Details selbst erarbeiten. Anstelle des zweiten Befehl kann man auch `git commit -a` ausführen, falls man an dieser Stelle ohnehin 'comitten' möchte. Siehe *git help ignore* um zu sehen, wie man Dateien definiert, die ignoriert werden sollen. Man kann das aber auch in einem einzigen Schritt ausführen mit: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove Die *-z* und *-0* Optionen verhindern unerwünschte Nebeneffekte durch Dateinamen mit ungewöhnlichen Zeichen. Da diese Anweisung aber auch zu ignorierende Dateien hinzufügt, kann man noch die `-x` oder `-X` Option hinzufügen. === Mein 'Commit' ist zu groß! === Hast Du es zu lange versäumt zu 'comitten'? Hast Du so versessen programmiert, dass Du darüber die Quellcodeverwaltung vergessen hast? Machst Du eine Serie von unabhängigen Änderungen, weil es Dein Stil ist? Keine Sorge, gib ein: $ git add -p Für jede Änderung, die Du gemacht hast, zeigt Git Dir die Codepassagen, die sich geändert haben und fragt, ob sie Teil des nächsten 'Commit' sein sollen. Antworte mit "y" für Ja oder "n" für Nein. Du hast auch noch andere Optionen, z.B. den Aufschub der Entscheidung; drücke "?" um mehr zu erfahren. Wenn Du zufrieden bist, gib $ git commit ein, um exakt die ausgewählten Änderungen zu 'comitten' (die "inszenierten" Änderungen). Achte darauf, nicht die Option *-a* einzusetzen, anderenfalls wird Git alle Änderungen 'comitten'. Was ist, wenn Du viele Dateien an verschiedenen Orten bearbeitet hast? Jede Datei einzeln nachzuprüfen, ist frustrierend und ermüdend. In diesem Fall verwende *git add -i*, dessen Bedienung ist nicht ganz einfach, dafür aber sehr flexibel. Mit ein paar Tastendrücken kannst Du mehrere geänderte Dateien für den 'Commit' hinzufügen ('stage') oder entfernen ('unstage') oder Änderungen einzelner Dateien nachprüfen und hinzufügen. Alternativ kannst Du *git commit \--interactive* verwenden, was dann automatisch die ausgewählten Änderungen 'commited', nachdem Du fertig bist. === Der Index: Git's Bereitstellungsraum === Bis jetzt haben wir Git's berühmten 'Index' gemieden, aber nun müssen wir uns mit ihm auseinandersetzen, um das bisherige zu erklären. Der Index ist ein temporärer Bereitstellungsraum. Git tauscht selten Daten direkt zwischen Deinem Projekt und seiner Versionsgeschichte aus. Vielmehr schreibt Git die Daten zuerst in den Index, danach kopiert es die Daten aus dem Index an ihren eigentlichen Bestimmungsort. Zum Beispiel ist *commit -a* eigentlich ein zweistufiger Prozess. Der erste Schritt erstellt einen Schnappschuss des aktuellen Status jeder überwachten Datei im Index. Der zweite Schritt speichert dauerhaft den Schnappschuss, der sich nun im Index befindet. Ein 'Commit' ohne die *-a* Option führt nur den zweiten Schritt aus und macht nur wirklich Sinn, wenn zuvor eine Anweisung angewendet wurde, welche den Index verändert, wie zum Beispiel *git add*. Normalerweise können wir den Index ignorieren und so tun, als würden wir direkt aus der Versionsgeschichte lesen oder in sie schreiben. In diesem Fall wollen wir aber mehr Kontrolle, also manipulieren wir den Index. Wir erstellen einen Schnappschuss einiger, aber nicht aller unser Änderungen im Index und speichern dann diesen sorgfältig zusammengestellten Schnappschuss permanent. === Verliere nicht Deinen KOPF === Der HEAD Bezeichner ist wie ein Cursor, der normalerweise auf den jüngsten 'Commit' zeigt und mit jedem neuen 'Commit' voranschreitet. Einige Git Anweisungen lassen Dich ihn manipulieren. Zum Beispiel: $ git reset HEAD~3 bewegt den HEAD Bezeichner drei 'Commits' zurück. Dadurch agieren nun alle Git Anweisungen, als hätte es die drei letzten 'Commits' nicht gegeben, während Deine Dateien unverändert erhalten bleiben. Siehe auf der Git Hilfeseite für einige Anwendungsbeispiele. Aber wie kannst Du zurück in die Zukunft? Die vergangenen 'Commits' wissen nichts von der Zukunft. Wenn Du den SHA1 Schlüssel vom originalen HEAD hast, dann: $ git reset 1b6d Aber stell Dir vor, Du hast ihn niemals notiert? Keine Sorge: Für solche Anweisungen sichert Git den original HEAD als Bezeichner mit dem Namen ORIG_HEAD und Du kannst gesund und munter zurückkehren mit: $ git reset ORIG_HEAD === KOPF-Jagd === Möglicherweise reicht ORIG_HEAD nicht aus. Vielleicht hast Du gerade bemerkt, dass Du einen kapitalen Fehler gemacht hast, und nun musst Du zu einem uralten 'Commit' in einem länst vergessenen 'Branch' zurück. Standardmäßig behält Git einen 'Commit' für mindesten zwei Wochen, sogar wenn Du Git anweist, den 'Branch' zu zerstören, in dem er enthalten ist. Das Problem ist, den entsprechenden SHA1-Wert zu finden. Du kannst Dir alle SHA1-Werte in `.git/objects` vornehmen und ausprobieren ob Du den gesuchten 'Commit' findest. Aber es gibt einen viel einfacheren Weg. Git speichert jeden errechneten SHA1-Wert eines 'Commits' in `.git/logs`. Das Unterverzeichnis `refs` enthält den Verlauf aller Aktivitäten auf allen 'Branches', während `HEAD` alle SHA1-Werte enthält, die jemals diese Bezeichnung hatten. Die letztere kann verwendet werden, um SHA1-Werte von 'Commits' zu finden, die sich in einem 'Branch' befanden, der versehentlich gestutzt wurde. Die reflog Anweisung bietet eine benutzerfreundliche Schnittstelle zu diesen Logdateien. Versuche $ git reflog Anstatt SHA1-Werte aus dem reflog zu kopieren und einzufügen, versuche: $ git checkout "@{10 minutes ago}" Oder rufe den fünftletzten 'Commit' ab, mit: $ git checkout "@{5}" Siehe in der ``Specifying Revisions'' Sektion von *git help rev-parse* für mehr. Vielleicht möchtest Du eine längere Gnadenfrist für todgeweihte 'Commits' konfigurieren. Zum Beispiel: $ git config gc.pruneexpire "30 days" bedeutet, ein gelöschter 'Commit' wird nur dann endgültig verloren sein, nachdem 30 Tage vergangen sind und *git gc* ausgeführt wurde. Du magst vielleicht auch das automatische Ausführen von *git gc* abstellen: $ git config gc.auto 0 wodurch 'Commits' nur noch gelöscht werden, wenn Du *git gc* manuell aufrufst. === Auf Git bauen === In echter UNIX Sitte erlaubt es Git's Design, dass es auf einfache Weise als Low-Level-Komponente von anderen Programmen benutzt werden kann, wie zum Beispiel grafischen Benutzeroberflächen und Internetanwendungen, alternative Kommandozeilenanwendungen, Patch-Werkzeugen, Import- und Konvertierungswerkzeugen und so weiter. Sogar einige Git Anweisungen selbst sind nur winzige Skripte, wie Zwerge auf den Schultern von Riesen. Mit ein bisschen Handarbeit kannst Du Git anpassen, damit es Deinen Anforderungen entspricht. Ein einfacher Trick ist es, die in Git integrierte Aliasfunktion zu verwenden, um die am häufigsten benutzten Anweisungen zu verkürzen: $ git config --global alias.co checkout $ git config --global --get-regexp alias # display current aliases alias.co checkout $ git co foo # same as 'git checkout foo' Etwas anderes ist der aktuelle 'Branch' im Prompt oder Fenstertitel. Die Anweisung $ git symbolic-ref HEAD zeigt den Namen des aktuellen 'Branch'. In der Praxis möchtest Du aber das "refs/heads/" entfernen und Fehler ignorieren: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- Das +contrib+ Unterverzeichnis ist eine Fundgrube von Werkzeugen, die auf Git aufbauen. Mit der Zeit können einige davon zu offiziellen Anweisungen befördert werden. Auf Debian und Ubuntu, findet man dieses Verzeichnis unter +/usr/share/doc/git-core/contrib+. Ein beliebter Vertreter ist +workdir/git-new-workdir+. Durch cleveres verlinken erzeugt dieses Skript ein neues Arbeitsverzeichis, das seine Versionsgeschichte mit dem original 'Repository' teilt: $ git-new-workdir ein/existierendes/repo neues/verzeichnis Das neue Verzeichnis und die Dateien darin kann man sich als 'Clone' vorstellen mit dem Unterschied, dass durch die gemeinschaftliche Versionsgeschichte die beiden Versionen automatisch synchron bleiben. Eine Synchronisierung mittels 'merge', 'push' oder 'pull' ist nicht notwendig. === Gewagte Kunststücke === Heutzutage macht es Git dem Anwender schwer, versehentlich Daten zu zerstören. Aber, wenn man weiß, was man tut, kann man die Schutzmaßnahmen der häufigsten Anweisungen umgehen. *Checkout*: Nicht versionierte Änderungen lassen 'checkout' scheitern. Um trotzdem die Änderungen zu zerstören und einen vorhandenen 'Commit' abzurufen, benutzen wir die 'force' Option: $ git checkout -f HEAD^ Auf der anderen Seite, wenn Du einen speziellen Pfad für 'checkout' angibst, gibt es keine Sicherheitsüberprüfungen mehr. Der angegebene Pfad wird stillschweigend überschrieben. Sei vorsichtig, wenn Du 'checkout' auf diese Weise benutzt. *Reset*: Reset versagt auch, wenn unversionierte Änderungen vorliegen. Um es zu erzwingen, verwende: $ git reset --hard 1b6d *Branch*: 'Branches' zu löschen scheitert ebenfalls, wenn dadurch Änderungen verloren gehen. Um das Löschen zu erzwingen, gib ein: $ git branch -D dead_branch # instead of -d Ebenso scheitert der Versuch, einen 'Branch' durch ein 'move' zu überschreiben, wenn das einen Datenverlust zur Folge hat. Um das Verschieben zu erzwingen, gib ein: $ git branch -M source target # instead of -m Anders als bei 'checkout' und 'reset' verschieben diese beiden Anweisungen das Zerstören der Daten. Die Änderungen bleiben im .git Unterverzeichnis gespeichert und können wieder hergestellt werden, wenn der entsprechende SHA1-Wert aus `.git/logs` ermittelt wird (siehe "KOPF-Jagd" oben). Standardmäßig bleiben die Daten mindestens zwei Wochen erhalten. *Clean*: Verschiedene git Anweisungen scheitern, weil sie Konflikte mit unversionierten Dateien vermuten. Wenn Du sicher bist, dass alle unversionierten Dateien und Verzeichnisse entbehrlich sind, dann lösche diese gnadenlos mit: $ git clean -f -d Beim nächsten Mal werden diese lästigen Anweisung gehorchen! === Verhindere schlechte 'Commits' === Dumme Fehler verschmutzen meine 'Repositories'. Am schrecklichsten sind fehlende Dateien wegen eines vergessenen *git add*. Kleinere Verfehlungen sind Leerzeichen am Zeilenende und ungelöste 'merge'-Konflikte: obwohl sie harmlos sind, wünschte ich, sie würden nie in der Öffentlichkeit erscheinen. Wenn ich doch nur eine Trottelversicherung abgeschlossen hätte, durch Verwendung eines _hook_, der mich bei solchen Problemen alarmiert. $ cd .git/hooks $ cp pre-commit.sample pre-commit # Older Git versions: chmod +x pre-commit Nun bricht Git einen 'Commit' ab, wenn es überflüssige Leerzeichen am Zeilenende oder ungelöste 'merge'-Konflikte entdeckt. Für diese Anleitung hätte ich vielleicht am Anfang des *pre-commit* 'hook' folgendes hinzugefügt, zum Schutz vor Zerstreutheit: if git ls-files -o | grep '\.txt$'; then echo FAIL! Untracked .txt files. exit 1 fi Viele Git Operationen unterstützen 'hooks'; siehe *git help hooks*. Wir haben den Beispiel 'hook' *post-update* aktiviert, weiter oben im Abschnitt Git über HTTP. Dieser läuft immer, wenn der 'HEAD' sich bewegt. Das Beispiel 'post-update' Skript aktualisiert Dateien, welche Git für die Kommunikation über 'Git-agnostic transports' wie z.B. HTTP benötigt. gitmagic-20160304/de/history.txt0000644000175000017500000002705612666307504015644 0ustar sbadiasbadia== Geschichtsstunde == Eine Folge von Git's verteilter Natur ist, dass die Chronik einfach verändert werden kann. Aber, wenn Du an der Vergangenheit manipulierst, sei vorsichtig: verändere nur den Teil der Chronik, den Du ganz alleine hast. So wie Nationen ewig diskutieren, wer welche Greueltaten vollbracht hat, wirst Du beim Abgleichen in Schwierigkeiten geraten, falls jemand einen 'Clone' mit abweichender Chronik hat und die Zweige sich austauschen sollen. Einige Entwickler setzen sich nachhaltig für die Unantastbarkeit der Chronik ein, mit allen Fehlern, Nachteilen und Mängeln. Andere denken, dass Zweige vorzeigbar gemacht werden sollten, bevor sie auf die Öffentlichkeit losgelassen werden. Git versteht beide Gesichtspunkte. Wie 'Clonen', 'Branchen' und 'Mergen' ist das Umschreiben der Chronik lediglich eine weitere Stärke, die Git Dir bietet. Es liegt an Dir, diese Weise zu nutzen. === Ich nehme alles zurück === Hast Du gerade 'commitet', aber Du hättest gerne eine andere Beschreibung eingegeben? Dann gib ein: $ git commit --amend um die letzte Beschreibung zu ändern. Du merkst, dass Du vergessen hast, eine Datei hinzuzufügen? Führe *git add* aus, um sie hinzuzufügen, und dann die vorhergehende Anweisung. Du willst noch ein paar Änderungen zu deinem letzten 'Commit' hinzufügen? Dann mache diese Änderungen und gib ein: $ git commit --amend -a === ... und noch viel mehr === Nehmen wir jetzt an, das vorherige Problem ist zehnmal schlimmer. Nach einer längeren Sitzung hast Du einen Haufen 'Commits' gemacht. Aber Du bist mit der Art der Organisation nicht glücklich, und einige 'Commits' könnten etwas umformuliert werden. Dann gib ein: $ git rebase -i HEAD~10 und die letzten zehn 'Commits' erscheinen in Deinem bevorzugten $EDITOR. Auszug aus einem Beispiel: pick 5c6eb73 Link repo.or.cz hinzugefügt pick a311a64 Analogien in "Arbeite wie Du willst" umorganisiert pick 100834f Push-Ziel zum Makefile hinzugefügt Dann: - Entferne 'Commits' durch das Löschen von Zeilen. - Organisiere 'Commits' durch verschieben von Zeilen. - Ersetze `pick` mit: * `edit`, um einen 'Commit' für 'amends' zu markieren. * `reword`, um die Log-Beschreibung zu ändern. * `squash`, um einen 'Commit' mit dem vorhergehenden zu vereinen ('merge'). * `fixup`, um einen 'Commit' mit dem vorhergehenden zu vereinen ('merge') und die Log-Beschreibung zu verwerfen. Speichere und Beende. Wenn Du einen 'Commit' mit 'edit' markiert hast, gib ein: $ git commit --amend Ansonsten: $ git rebase --continue Also 'commite' früh und oft: Du kannst später mit 'rebase' aufräumen. === Lokale Änderungen zum Schluss === Du arbeitest an einem aktiven Projekt. Über die Zeit haben sich einige lokale 'Commits' angesammelt und dann synchronisierst Du mit einem 'Merge' mit dem offiziellen Zweig. Dieser Zyklus wiederholt sich ein paar Mal, bevor Du zum 'Pushen' in den zentralen Zweig bereit bist. Aber nun ist die Chronik in deinem lokalen Git-'Clone' ein chaotisches Durcheinander deiner Änderungen und den Änderungen vom offiziellen Zweig. Du willst alle Deine Änderungen lieber in einem fortlaufenden Abschnitt und hinter den offiziellen Änderungen sehen. Das ist eine Aufgabe für *git rebase*, wie oben beschrieben. In vielen Fällen kannst Du den *--onto* Schalter benutzen, um Interaktion zu vermeiden. Siehe auch *git help rebase* für ausführliche Beispiele dieser erstaunlichen Anweisung. Du kannst auch 'Commits' aufteilen. Du kannst sogar 'Branches' in einem 'Repository' umorganisieren. === Chronik umschreiben === Gelegentlich brauchst Du Versionsverwaltung vergleichbar dem Wegretuschieren von Personen aus einem offiziellen Foto, um diese in stalinistischer Art aus der Geschichte zu löschen. Stell Dir zum Beispiel vor, Du willst ein Projekt veröffentlichen, aber es enthält eine Datei, die aus irgendwelchen Gründen privat bleiben muss. Vielleicht habe ich meine Kreditkartennummer in einer Textdatei notiert und diese versehentlich dem Projekt hinzugefügt. Die Datei zu löschen, ist zwecklos, da über ältere 'Commits' auf sie zugegriffen werden könnte. Wir müssen die Datei aus allen 'Commits' entfernen: $ git filter-branch --tree-filter 'rm sehr/geheime/Datei' HEAD Siehe *git help filter-branch*, wo dieses Beispiel erklärt und eine schnellere Methode vorstellt wird. Allgemein, *filter-branch* lässt Dich große Bereiche der Chronik mit einer einzigen Anweisung verändern. Danach beschreibt der Ordner +.git/refs/original+ den Zustand der Lage vor der Operation. Prüfe, ob die 'filter-branch' Anweisung getan hat, was Du wolltest, dann lösche dieses Verzeichnis, bevor Du weitere 'filter-branch' Operationen durchführst. Zuletzt, ersetze alle 'Clones' Deines Projekts mit Deiner überarbeiteten Version, falls Du später mit ihnen interagieren möchtest. === Geschichte machen === [[makinghistory]] Du möchtest ein Projekt zu Git umziehen? Wenn es mit einem der bekannteren Systeme verwaltet wird, besteht die Möglichkeit, dass schon jemand ein Skript geschrieben hat, das die gesamte Chronik für Git exportiert. Anderenfalls, sieh dir *git fast-import* an, das Text in einem speziellen Format einliest, um eine Git Chronik von Anfang an zu erstellen. Normalerweise wird ein Skript, das diese Anweisung benutzt, hastig zusammengeschustert und einmalig ausgeführt, um das Projekt in einem einzigen Lauf zu migrieren. Erstelle zum Beispiel aus folgendem Listing eine temporäre Datei, z.B. `/tmp/history`: ---------------------------------- commit refs/heads/master committer Alice Thu, 01 Jan 1970 00:00:00 +0000 data < int main() { printf("Hallo, Welt!\n"); return 0; } EOT commit refs/heads/master committer Bob Tue, 14 Mar 2000 01:59:26 -0800 data < int main() { write(1, "Hallo, Welt!\n", 14); return 0; } EOT ---------------------------------- Dann, erstelle ein Git 'Repository' aus dieser temporären Datei, durch Eingabe von: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history Die aktuellste Version des Projekts kannst Du abrufen ('checkout') mit: $ git checkout master . Die Anweisung *git fast-export* konvertiert jedes 'Repository' in das *git fast-import* Format, dessen Ausgabe Du studieren kannst, um Exporte zu schreiben und außerdem, um 'Repositories' im menschenlesbaren Text zu übertragen. Tatsächlich können diese Anweisungen Klartext-'Repositories' über reine Textkanäle übertragen. === Wo ging alles schief? === Du hast gerade eine Funktion in Deiner Anwendung entdeckt, die nicht mehr funktioniert und Du weißt sicher, dass sie vor ein paar Monaten noch ging. Argh! Wo kommt dieser Fehler her? Hättest Du nur die Funktion während der Entwicklung getestet. Dafür ist es nun zu spät. Wie auch immer, vorausgesetzt Du hast oft 'comittet', kann Git Dir sagen, wo das Problem liegt: $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git ruft einen Stand ab, der genau dazwischen liegt. Teste die Funktion und wenn sie immer noch nicht funktioniert: $ git bisect bad Wenn nicht, ersetzte "bad" mit "good". Git versetzt Dich wieder auf einen Stand genau zwischen den bekannten Versionen "good" und "bad" und reduziert so die Möglichkeiten. Nach ein paar Durchläufen wird Dich diese binäre Suche zu dem 'Commit' führen, der die Probleme verursacht. Wenn Du deine Ermittlungen abgeschlossen hast, kehre zum Originalstand zurück mit: $ git bisect reset Anstatt jede Änderung per Hand zu untersuchen, automatisiere die Suche durch Ausführen von: $ git bisect run mein_skript Git benutzt den Rückgabewert der übergebenen Anweisung, normalerweise ein Skript für einmalige Ausführung, um zu entscheiden, ob eine Änderung gut ('good') oder schlecht ('bad') ist: Das Skript sollte 0 für 'good' zurückgeben, 125 wenn die Änderung übersprungen werden soll und irgendetwas zwischen 1 und 127 für 'bad'. Ein negativer Rückgabewert beendet die 'bisect'-Operation sofort. Du kannst noch viel mehr machen: die Hilfe erklärt, wie man 'bisect'-Operationen visualisiert, das 'bisect'-Log untersucht oder wiedergibt und sicher unschuldige Änderungen ausschließt, um die Suche zu beschleunigen. === Wer ist verantwortlich? === Wie viele andere Versionsverwaltungssysteme hat Git eine 'blame' Anweisung: $ git blame bug.c das jede Zeile in der angegebenen Datei kommentiert, um anzuzeigen, wer sie zuletzt geändert hat und wann. Im Gegensatz zu vielen anderen Versionsverwaltungssystemen funktioniert diese Operation offline, es wird nur von der lokalen Festplatte gelesen. === Persönliche Erfahrungen === In einem zentralisierten Versionsverwaltungssystem ist das Bearbeiten der Chronik eine schwierige Angelegenheit und den Administratoren vorbehalten. 'Clonen', 'Branchen' und 'Mergen' sind unmöglich ohne Netzwerkverbindung. Ebenso grundlegende Funktionen wie das Durchsuchen der Chronik oder das 'comitten' einer Änderung. In manchen Systemen benötigt der Anwender schon eine Netzwerkverbindung, nur um seine eigenen Änderungen zu sehen oder um eine Datei zum Bearbeiten zu öffnen. Zentralisierte Systeme schließen es aus, offline zu arbeiten und benötigen teurere Netzwerkinfrastruktur, vor allem, wenn die Zahl der Entwickler steigt. Am wichtigsten ist, dass alle Operationen bis zu einem gewissen Grad langsamer sind, in der Regel bis zu dem Punkt, wo Anwender erweiterte Anweisungen scheuen, bis sie absolut notwendig sind. In extremen Fällen trifft das auch auf die grundlegenden Anweisungen zu. Wenn Anwender langsame Anweisungen ausführen müssen, sinkt die Produktivität, da der Arbeitsfluss unterbrochen wird. Ich habe diese Phänomen aus erster Hand erfahren. Git war das erste Versionsverwaltungssystem, das ich benutzt habe. Ich bin schnell in die Anwendung hineingewachsen und betrachtete viele Funktionen als selbstverständlich. Ich habe einfach vorausgesetzt, dass andere Systeme ähnlich sind: die Auswahl eines Versionsverwaltungssystems sollte nicht anders sein als die Auswahl eines Texteditors oder Internetbrowser. Ich war geschockt, als ich später gezwungen war, ein zentralisiertes System zu benutzen. Eine unzuverlässige Internetverbindung stört mit Git nicht sehr, aber sie macht die Entwicklung unerträglich, wenn sie so zuverlässig wie ein lokale Festplatte sein sollte. Zusätzlich habe ich mich dabei ertappt, bestimmte Anweisungen zu vermeiden, um die damit verbundenen Wartezeiten zu vermeiden, und das hat mich letztendlich davon abgehalten, meinem gewohnten Arbeitsablauf zu folgen. Wenn ich eine langsame Anweisung auszuführen hatte, wurde durch die Unterbrechung meiner Gedankengänge dem Arbeitsfluss ein unverhältnismäßiger Schaden zugefügt. Während des Wartens auf das Ende der Serverkommunikation tat ich etwas anderes, um die Wartezeit zu überbrücken, zum Beispiel E-Mails lesen oder Dokumentation schreiben. Wenn ich zur ursprünglichen Arbeit zurückkehrte, war die Operation längst beendet und ich vergeudete noch mehr Zeit beim Versuch, mich zu erinnern, was ich getan habe. Menschen sind nicht gut im Kontextwechsel. Da war auch ein interessanter http://de.wikipedia.org/wiki/Tragik_der_Allmende[Tragik-der-Allmende] Effekt: Netzwerküberlastungen erahnend, verbrauchten einzelne Individuen für diverse Operationen mehr Netzwerkbandbreite als erforderlich, um zukünftige Engpässe zu vermeiden. Die Summe der Bemühungen verschlimmerte die Überlastungen, was einzelne wiederum ermutigte, noch mehr Bandbreite zu verbrauchen, um noch längere Wartezeiten zu verhindern. gitmagic-20160304/de/translate.txt0000644000175000017500000000273112666307504016131 0ustar sbadiasbadia== Anhang B: Diese Anleitung übersetzen == Ich empfehle folgende Schritte, um diese Anleitung zu übersetzen, damit meine Skripte einfach eine HTML- und PDF-Version erstellen können. Außerdem können so alle Übersetzungen in einem 'Repository' existieren. 'Clone' die Quelltexte, dann erstelle ein Verzeichnis mit dem Namen des IETF Sprachkürzel der übersetzten Sprache: siehe http://www.w3.org/International/articles/language-tags/Overview.en.php[den W3C Artikel über Internationalisierung]. Zum Beispiel, Englisch ist "en", Japanisch ist "ja". Kopiere alle +txt+-Dateien aus dem "en"-Verzeichnis in das neue Verzeichnis und übersetze diese. Um zum Beispiel die Anleitung auf http://de.wikipedia.org/wiki/Klingonische_Sprache[Klingonisch] zu übersetzen, musst du folgendes machen: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" ist das IETF Sprachkürzel für Klingonisch. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # übersetze diese Datei. und das machst du für jede txt-Datei. Bearbeite das Makefile und füge das Sprachkürzel zur Variable `TRANSLATIONS` hinzu. Nun kannst Du Deine Arbeit jederzeit wie folgt überprüfen: $ make tlh $ firefox book-tlh/index.html 'Committe' Deine Änderungen oft, und wenn Du fertig bist, gib bitte Bescheid. GitHub hat eine Schnittstelle, die das erleichtert: Erzeuge Deinen eigene 'Fork' vom "gitmagic" Projekt, 'pushe' Deine Änderungen, dann gib mir Bescheid, Deine Änderungen zu 'mergen'. gitmagic-20160304/de/intro.txt0000644000175000017500000001706412666307504015274 0ustar sbadiasbadia== Einleitung == Ich benutze eine Analogie, um in die Versionsverwaltung einzuführen. Für eine vernünftigere Erklärung siehe http://de.wikipedia.org/wiki/Versionsverwaltung[den Wikipedia Artikel zur Versionsverwaltung]. === Arbeit ist Spiel === Ich spiele Computerspiele schon fast mein ganzes Leben. Im Gegensatz dazu habe ich erst als Erwachsener damit begonnen, Versionsverwaltungssysteme zu benutzen. Ich vermute, dass ich damit nicht alleine bin, und der Vergleich hilft vielleicht dabei, die Konzepte einfacher zu erklären und zu verstehen. Stelle Dir das Bearbeiten deines Codes oder Deiner Dokumente wie ein Computerspiel vor. Wenn Du gut vorangekommen bist, willst Du das Erreichte sichern. Um das zu tun, klickst Du auf auf Speichern in Deinem vertrauten Editor. Aber das überschreibt die vorherige Version. Das ist wie bei den Spielen der alten Schule, die nur Speicherplatz für eine Sicherung hatten: sicherlich konntest Du speichern, aber Du konntest nie zu einem älteren Stand zurück. Das war eine Schande, denn vielleicht war Deine vorherige Sicherung an einer außergewöhnlich spannenden Stelle des Spiels, zu der Du später gerne noch einmal zurückkehren möchtest. Oder noch schlimmer, Deine aktuelle Sicherung ist in einem nicht lösbaren Stand, dann musst Du von ganz vorne beginnen. === Versionsverwaltung === Beim Editieren kannst Du Deine Datei durch 'Speichern unter ...' mit einem neuen Namen abspeichern oder Du kopierst sie vor dem Speichern irgendwo hin, um die alte Version zu erhalten. Außerdem kannst Du sie komprimieren, um Speicherplatz zu sparen. Das ist eine primitive und mühselige Form der Versionsverwaltung. Computerspiele machten das lange Zeit so, viele von ihnen hatten automatisch erstellte Sicherungspunkte mit Zeitstempel. Jetzt lass uns das Problem etwas komplizierter machen. Sagen wir, Du hast einen Haufen Dateien, die zusammen gehören, z.B. Quellcodes für ein Projekt oder Dateien einer Website. Wenn Du nun eine alte Version erhalten willst, musst Du den ganzen Ordner archivieren. Viele Versionen auf diese Art zu archivieren, ist mühselig und kann sehr schnell teuer werden. Bei einigen Computerspielen bestand ein gesicherter Stand wirklich aus einem Ordner voller Dateien. Diese Spiele versteckten die Details vor dem Spieler und präsentierten eine bequeme Oberfläche, um verschiedene Versionen des Ordners zu verwalten. Versionsverwaltungen sind nicht anders. Sie alle haben bequeme Schnittstellen, um Ordner voller Dateien zu verwalten. Du kannst den Zustand des Ordners sichern, so oft Du willst, und du kannst später jeden Sicherungspunkt wieder herstellen. Im Gegensatz zu den meisten Computerspielen sind sie aber in der Regel dafür ausgelegt, sparsam mit dem Speicherplatz umzugehen. Normalerweise ändern sich immer nur wenige Dateien zwischen zwei Versionen, und die Änderungen selbst sind oft nicht groß. Die Platzersparnis beruht auf dem Speichern der Unterschiede an Stelle einer Kopie der ganzen Datei. === Verteilte Kontrolle === Nun stell Dir ein ganz kompliziertes Computerspiel vor. So schwierig zu lösen, dass viele erfahrene Spieler auf der ganzen Welt beschließen, sich zusammen zu tun und ihre gespeicherten Spielstände auszutauschen, um das Spiel zu beenden. 'Speedruns' sind Beispiele aus dem echten Leben: Spieler, die sich in unterschiedlichen Spielebenen des selben Spiels spezialisiert haben, arbeiten zusammen, um erstaunliche Ergebnisse zu erzielen. Wie würdest Du ein System erstellen, bei dem jeder auf einfache Weise die Sicherungen der anderen bekommt? Und eigene Sicherungen bereitstellt? Früher nutzte jedes Projekt eine zentralisierte Versionsverwaltung. Irgendwo speicherte ein Server alle gespeicherten Spiele, sonst niemand. Jeder Spieler hatte nur ein paar gespeicherte Spiele auf seinem Rechner. Wenn ein Spieler einen Fortschritt machen wollte, musste er den aktuellsten Stand vom Hauptserver herunterladen, eine Weile spielen, sichern und den Stand dann wieder auf den Server laden, damit irgendjemand ihn nutzen kann. Was, wenn ein Spieler aus irgendeinem Grund einen alten Spielstand will? Vielleicht ist der aktuell gesicherte Spielstand nicht mehr lösbar, weil jemand in der dritten Ebene vergessen hat, ein Objekt aufzunehmen und sie versuchen, den letzten Spielstand zu finden, ab dem das Spiel wieder lösbar ist. Oder sie wollen zwei Spielstände vergleichen, um festzustellen, wie viel ein Spieler geleistet hat. Es gibt viele Gründe, warum man einen älteren Stand sehen will, aber das Ergebnis ist das selbe. Man muss vom Hauptserver das alte gespeicherte Spiel anfordern. Je mehr gespeicherte Spiele benötigt werden, desto mehr Kommunikation ist erforderlich. Die neue Generation der Versionsverwaltungssysteme, zu denen Git gehört, werden verteilte Systeme genannt und können als eine Verallgemeinerung der zentralisierten Systeme verstanden werden. Wenn Spieler vom Hauptserver herunterladen, erhalten sie jedes gespeichertes Spiel, nicht nur das zuletzt gespeicherte. Es ist, als ob der Hauptserver gespiegelt wird. Dieses erste 'Cloning' kann teuer sein, vor allem, wenn eine lange Geschichte existiert, aber auf Dauer wird es sich lohnen. Ein unmittelbarer Vorteil ist, wenn aus irgendeinem Grund ein älterer Stand benötigt wird, ist keine Kommunikation mit dem Hauptserver notwendig. === Ein dummer Aberglaube === Ein weit verbreitetes Missverständnis ist, dass verteilte System ungeeignet sind für Projekte, die ein offizielles zentrales 'Repository' benötigen. Nichts könnte weiter von der Wahrheit entfernt sein. Jemanden zu fotografieren, stiehlt nicht dessen Seele. Genauso wenig setzt das 'Clonen' des zentralen 'Repository' dessen Bedeutung herab. Eine gute erste Annäherung ist, dass alles, was eine zentralisierte Versionsverwaltung kann, ein gut durchdachtes verteiltes System besser kann. Netzwerkressourcen sind einfach teurer als lokale Ressourcen. Auch wenn wir später Nachteile beim verteilten Ansatz sehen werden, ist man mit dieser Faustregel weniger anfällig für falsche Vergleiche. Ein kleines Projekt mag nur einen Bruchteil der Möglichkeiten benötigen, die so ein System bietet. Aber deshalb ein einfacheres, schlecht erweiterbares System zu benutzen, ist wie römische Ziffern zum Rechnen mit kleinen Zahlen zu verwenden. Außerdem könnte Dein Projekt weit über die ursprünglichen Erwartungen hinauswachsen. Git von Anfang an zu benutzen, ist wie ein Schweizer Taschenmesser mit sich zu tragen, auch wenn damit meistens nur Flaschen geöffnet werden. Eines Tages brauchst Du vielleicht dringend einen Schraubendreher, dann bist du froh, mehr als nur einen einfachen Flaschenöffner bei dir zu haben. === 'Merge' Konflikte === Für diesen Punkt ist unsere Computerspielanalogie ungeeignet. Stattdessen stellen wir uns wieder vor, wir editieren ein Dokument. Stell dir vor, Alice fügt eine Zeile am Dateianfang hinzu und Bob eine am Dateiende. Beide laden ihre Änderungen hoch. Die meisten Systeme wählen automatisch eine vernünftige Vorgehensweise: akzeptiere beide Änderungen und füge sie zusammen, damit fließen beide Änderungen in das Dokument mit ein. Nun stell Dir vor beide, Alice und Bob, machen Änderungen in der selben Zeile. Dann ist es unmöglich, ohne menschlichen Eingriff fortzufahren. Die zweite Person, welche die Datei hoch lädt, wird über einen _'Merge' Konflikt_ informiert und muss entscheiden, welche Änderung übernommen wird oder die ganze Zeile überarbeiten. Es können noch weitaus kompliziertere Situationen entstehen. Versionsverwaltungssysteme behandeln die einfacheren Fälle selbst und überlassen die schwierigen uns Menschen. Üblicherweise ist deren Verhalten einstellbar. gitmagic-20160304/de/clone.txt0000644000175000017500000002756512666307504015250 0ustar sbadiasbadia== Rund ums 'Clonen' == In älteren Versionsverwaltungssystemen ist 'checkout' die Standardoperation, um Dateien zu bekommen. Du bekommst einen Haufen Dateien eines bestimmten Sicherungsstands. In Git und anderen verteilten Versionsverwaltungssystemen ist 'clone' die Standardaktion. Um Dateien zu bekommen, erstellst Du einen 'Clone' des gesamten 'Repository'. Oder anders gesagt, Du spiegelst den zentralen Server. Alles, was man mit dem zentralen 'Repository' tun kann, kannst Du auch mit deinem 'Clone' tun. === Computer synchronisieren === Es ist akzeptabel, für Datensicherungen und einfaches Synchronisieren mit 'tarball' Archiven oder *rsync* zu arbeiten. Aber manchmal arbeite ich an meinem Laptop, dann an meinem Desktop-PC, und die beiden haben sich inzwischen nicht austauschen können. Erstelle ein Git 'Repository' und 'commite' Deine Dateien auf dem einen Rechner. Dann auf dem anderen: $ git clone anderer.computer:/pfad/zu/dateien um eine zweite Kopie der Dateien und des Git 'Repository' zu erstellen. Von jetzt an wird $ git commit -a $ git pull anderer.computer:/pfad/zu/dateien HEAD den Zustand der Dateien des anderen Computer auf den übertragen, an dem Du gerade arbeitest. Solltest Du kürzlich konkurrierende Änderungen an der selben Datei vorgenommen haben, lässt Git Dich das wissen, und Du musst erneut 'commiten', nachdem Du die Konflikte aufgelöst hast. === Klassische Quellcodeverwaltung === Erstelle ein Git 'Repository' für Deine Dateien: $ git init $ git add . $ git commit -m "Erster Commit" Auf dem zentralen Server erstelle ein 'bare Repository' in irgendeinem Ordner: $ mkdir proj.git $ cd proj.git $ git init --bare $ touch proj.git/git-daemon-export-ok Wenn nötig, starte den Git-Dämon: $ git daemon --detach # er könnte schon laufen Für Git Hostingdienste folge den Anweisungen zum Erstellen des zunächst leeren Git 'Repository'. Normalerweise füllt man ein Formular auf einer Website aus. Übertrage ('push') dein Projekt auf den zentralen Server mit: $ git push zentraler.server/pfad/zu/proj.git HEAD Um die Quellcodes abzurufen, gibt ein Entwickler folgendes ein: $ git clone zentraler.server/pfad/zu/proj.git Nach dem Bearbeiten sichert der Entwickler die Änderungen lokal: $ git commit -a Um auf die aktuelle Server-Version zu aktualisieren: $ git pull Irgendwelche 'Merge'-Konflikte sollten dann aufgelöst und erneut 'commitet' werden: $ git commit -a Um die lokalen Änderungen in das zentrale 'Repository' zu übertragen: $ git push Wenn inzwischen neue Änderungen von anderen Entwicklern beim Hauptserver eingegangen sind, schlägt Dein 'push' fehl. Aktualisiere das lokale 'Repository' erneut mit 'pull', löse eventuell aufgetretene 'Merge'-Konflikte und versuche es nochmal. Entwickler brauchen SSH Zugriff für die vorherigen 'pull' und 'push' Anweisungen. Trotzdem kann jedermann die Quelltexte einsehen, durch Eingabe von: $ git clone git://zentraler.server/pfad/zu/proj.git Das ursprüngliche Git-Protokoll ähnelt HTTP: Es gibt keine Authentifizierung, also kann jeder das Projekt abrufen. Folglich ist standardmäßig das 'Pushen' per Git-Protokoll verboten. === Geheime Quellen === Für ein Closed-Source-Projekt lasse die 'touch' Anweisung weg und stelle sicher, dass niemals eine Datei namens `git-daemon-export-ok` erstellt wird. Das 'Repository' kann nun nicht mehr über das Git-Protokol abgerufen werden; nur diejenigen mit SSH Zugriff können es einsehen. Wenn alle 'Repositories' geschlossen sind, ist es unnötig den Git Dämon laufen zu lassen, da jegliche Kommunikation über SSH läuft. === 'Nackte Repositories' === Ein nacktes ('bare') 'Repository' wird so genannt, weil es kein Arbeitsverzeichnis hat. Es enthält nur Dateien, die normalerweise im '.git' Unterverzeichnis versteckt sind. Mit anderen Worten, es verwaltet die Geschichte eines Projekts, enthält aber niemals einen Auszug irgendeiner beliebigen Version. Ein 'bare Repository' übernimmt die Rolle des Hauptserver in einem zentralisierten Versionsverwaltungssystem: Das Zuhause Deines Projekts. Entwickler 'clonen' Dein Projekt davon und 'pushen' die letzten offiziellen Änderungen dort hin. Meistens befindet es sich auf einem Server, der nicht viel tut außer Daten zu verbreiten. Die Entwicklung findet in den 'Clonen' statt, so kann das Heim-'Repository' ohne Arbeitsverzeichnis auskommen. Viele Git Befehle funktionieren nicht in 'bare Repositories'. Es sei denn, die `GIT_DIR` Umgebungsvariable wird auf das Arbeitsverzeichnis gesetzt, oder die `--bare` Option wird übergeben. === 'Push' oder 'Pull' === Warum haben wir den 'push'-Befehl eingeführt, anstatt bei dem vertrauten 'pull'-Befehl zu bleiben? Zuerst, 'pull' funktioniert nicht mit 'bare Repositories': stattdessen benutze 'fetch', ein Befehl, den wir später behandeln. Aber auch wenn wir ein normales 'Repository' auf dem zentralen Server halten würden, wäre das 'pullen' eine mühselige Angelegenheit. Wir müssten uns zuerst in den Server einloggen und dem 'pull'-Befehl die Netzwerkadresse des Computer übergeben, von dem aus wir die Änderungen 'pullen', also abholen wollen. Firewalls könnten uns stören und was, wenn wir gar keine Berechtigung für eine Serverkonsole haben. Wie auch immer, abgesehen von diesem Fall, raten wir vom 'Pushen' in ein 'Repository' ab. Falls das Ziel nämlich ein Arbeitsverzeichnis hat, können Verwirrungen entstehen. Kurzum, während du lernst mit Git umzugehen, 'pushe' nur, wenn das Ziel ein 'bare Repository' ist, andernfalls benutze 'pull'. === 'Fork' eines Projekts === Hast Du es satt, wie sich ein Projekt entwickelt? Du denkst, Du kannst das besser? Dann mache folgendes auf deinem Server: $ git clone git://haupt.server/pfad/zu/dateien Dann erzähle jedem von Deinem 'Fork' des Projekts auf Deinem Server. Zu jedem späteren Zeitpunkt kannst du die Änderungen des Originalprojekts 'mergen' mit: $ git pull === Ultimative Datensicherung === Du willst zahlreiche, vor Manipulation geschützte, redundante Datensicherungen an unterschiedlichen Orten? Wenn Dein Projekt viele Entwickler hat, musst Du nichts tun! Jeder 'Clone' Deines Codes ist eine vollwertige Datensicherung. Nicht nur des aktuellen Stand, sondern die gesamte Geschichte. Wird irgendein 'Clone' beschädigt, wird dies dank des kryptographischen 'Hashing' sofort erkannt, sobald derjenige versucht, mit anderen zu kommunizieren. Wenn Dein Projekt nicht so bekannt ist, finde so viele Server, wie Du kannst, um dort einen 'Clone' zu platzieren. Die wirklich Paranoiden sollten immer den letzten 20-Byte SHA1 Hash des 'HEAD' aufschreiben und an einem sicheren Ort aufbewahren. Er muss sicher sein, aber nicht privat. Zum Beispiel wäre es sicher, ihn in einer Zeitung zu veröffentlichen, denn es ist schwer für einen Angreifer jede Zeitungskopie zu manipulieren. === Multitasking mit Lichtgeschwindigkeit === Nehmen wir an Du willst parallel an mehreren Funktionen arbeiten. Dann 'commite' Dein Projekt und gib ein: $ git clone . /irgendein/neuer/ordner http://de.wikipedia.org/wiki/Harter_Link[Harten Links] ist es zu verdanken, dass ein lokaler Klon weniger Zeit und Speicherplatz benötigt als eine herkömmliche Datensicherung. Du kannst nun an zwei unabhängigen Funktionen gleichzeitig arbeiten. Zum Beispiel kannst Du an einen Klon bearbeiten, während der andere kompiliert wird. Zu jeder Zeit kannst Du 'comitten' und die Änderungen des anderen Klon 'pullen'. $ git pull /der/andere/clone HEAD === Versionsverwaltung im Untergrund === Arbeitest Du an einem Projekt, das ein anderes Versionsverwaltungssystem nutzt und vermisst Du Git? Dann erstelle ein Git 'Repository' in deinem Arbeitsverzeichnis: $ git init $ git add . $ git commit -m "Erster Commit" dann 'Clone' es: $ git clone . /irgendein/neuer/ordner Nun gehe in das neue Verzeichnis und arbeite dort mit Git nach Herzenslust. Irgendwann wirst Du dann mit den anderen synchronisieren wollen, dann gehe in das Originalverzeichnis, aktualisiere mit dem anderen Versionsverwaltungssystem und gib ein: $ git add . $ git commit -m "Synchronisation mit den anderen" Dann gehe wieder ins neue Verzeichnis und gib ein: $ git commit -a -m "Beschreibung der Änderungen" $ git pull Die Vorgehensweise, wie Du Deine Änderungen den anderen übergibst, hängt vom anderen Versionsverwaltungssystem ab. Das neue Verzeichnis enthält die Dateien mit deinen Änderungen. Führe die Anweisungen des anderen Versionsverwaltungssystems aus, die nötig sind, um die Dateien ins zentrale 'Repository' zu übertragen. Subversion, vielleicht das beste zentralisierte Versionsverwaltungssystem, wird von unzähligen Projekten benutzt. Der *git svn*-Befehl automatisiert den zuvor genannten Ablauf für Subversion 'Repositories' und kann auch benutzt werden, um http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[ein Git Projekt in ein Subversion 'Repository' zu exportieren]. === Mercurial === Mercurial ist ein ähnliches Versionsverwaltungssystem, das fast nahtlos mit Git zusammenarbeiten kann. Mit der `hg-git`-Erweiterung kann ein Benutzer von Mercurial verlustfrei in ein Git 'Repository' 'pushen' und daraus 'pullen'. Beschaffe Dir die `hg-git`-Erweiterung mit Git: $ git clone git://github.com/schacon/hg-git.git oder Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ Leider kenne ich keine solche Erweiterung für Git. Aus diesem Grund plädiere ich für Git statt Mercurial für ein zentrales 'Repository', auch wenn man Mercurial bevorzugt. Bei einem Mercurial Projekt gibt es gewöhnlich immer einen Freiwilligen, der parallel dazu ein Git 'Repository' für die Git Anwender unterhält, wogegen, Dank der `hg-git`-Erweiterung, ein Git Projekt automatisch die Benutzer von Mercurial mit einbezieht. Die Erweiterung kann auch ein Mercurial 'Repository' in ein Git 'Repository' umwandeln, indem man in ein leeres 'Repository' 'pushed'. Einfacher geht das mit dem `hg-fast-export.sh` Skript, welches es hier gibt: $ git clone git://repo.or.cz/fast-export.git Zum Konvertieren gib in einem leeren Verzeichnis ein: $ git init $ hg-fast-export.sh -r /hg/repo nachdem Du das Skript zu deinem `$PATH` hinzugefügt hast. === Bazaar === Wir erwähnen auch kurz Bazaar, weil es nach Git und Mercurial das bekannteste freie verteilte Versionsverwaltungssystem ist. Bazaar hat den Vorteil des Rückblicks, da es relativ jung ist; seine Entwickler konnten aus Fehlern der Vergangenheit lernen und kleine historische Unwegbarkeiten umgehen. Außerdem waren sich die Entwickler der Popularität und Interoperabilität mit anderen Versionsverwaltungssystemen bewusst. Eine `bzr-git`-Erweiterung lässt Anwender von Bazaar einigermaßen mit Git 'Repositories' arbeiten. Das `tailor` Programm konvertiert Bazaar 'Repositories' zu Git 'Repositories' und kann das forlaufend tun, während `bzr-fast-export` für einmalige Konvertierungen besser geeignet ist. === Warum ich Git benutze === Ich habe ursprünglich Git gewählt, weil ich gehört habe, dass es die unvorstellbar unüberschaubaren Linux Kernel Quellcodes verwalten kann. Ich hatte noch keinen Grund zu wechseln. Git hat mir bewundernswert gedient und hat mich bis jetzt noch nie im Stich gelassen. Da ich in erster Linie unter Linux arbeite, sind Probleme anderer Plattformen bedeutungslos. Ich bevorzuge auch C-Programme und 'bash'-Skripte gegenüber Anwendungen wie zum Beispiel Python Skripts: Es gibt weniger Abhängigkeiten und ich bin süchtig nach schellen Ausführungszeiten. Ich dachte darüber nach, wie Git verbessert werden könnte, ging sogar so weit, dass ich meine eigene Git-Ähnliche Anwendung schrieb, allerdings nur als akademische Übungen. Hätte ich mein Projekt fertig gestellt, wäre ich trotzdem bei Git geblieben, denn die Verbesserungen wären zu gering gewesen, um den Einsatz eines Eigenbrödler-Systems zu rechtfertigen. Natürlich können Deine Bedürfnisse und Wünsche ganz anders sein und vielleicht bist Du mit einem anderen System besser dran. Wie auch immer, mit Git kannst Du nicht viel falsch machen. gitmagic-20160304/zh_cn/0000755000175000017500000000000012666307504014101 5ustar sbadiasbadiagitmagic-20160304/zh_cn/drawbacks.txt0000644000175000017500000001522612666307504016611 0ustar sbadiasbadia== 附录 A: Git的缺点 == 有一些Git的问题,我已经藏在毯子下面了。有些可以通过脚本或回调方法轻易地解决, 有些需要重组或重定义项目,少数剩下的烦恼,还只能等待。或者更好地,投入进来帮 忙。 === SHA1 的弱点 === 随着时间的推移,密码学家发现越来越多的SHA1的弱点。已经发现对对资源雄厚的组织 哈希冲撞是可能的。在几年内,或许甚至一个一般的PC也将有足够计算能力悄悄摧毁一 个Git仓库。 希望在进一步研究摧毁SHA1之前,Git能迁移到一个更好的哈希算法。 === 微软 Windows === Git在微软Windows上可能有些繁琐: - http://cygwin.com/[Cygwin] ,, 一个Windows下的类Linux的环境,包含一个 http://cygwin.com/packages/git/[ 一个Git在Windows下的移植]. - http://code.google.com/p/msysgit/[基于MSys的Git] 是另一个,要求最小运行时支持,不过一些命令不能马上工作。 === 不相关的文件 === 如果你的项目非常大,包含很多不相关的文件,而且正在不断改变,Git可能比其他系统 更不管用,因为独立的文件是不被跟踪的。Git跟踪整个项目的变更,这通常才是有益的。 一个方案是将你的项目拆成小块,每个都由相关文件组成。如果你仍然希望在同一个资 源库里保存所有内容的话,可以使用 *git submodule* 。 === 谁在编辑什么? === 一些版本控制系统在编辑前强迫你显示地用某个方法标记一个文件。尽管这种要求很烦 人,尤其是需要和中心服务器通讯时,不过它还是有以下两个好处的: 1. 比较速度快,因为只有被标记的文件需要检查。 2. 可以知道谁在这个文件上工作,通过查询在中心服务器谁把这个文件标记为编辑状 态。 使用适当的脚本,你也可以使Git达到同样的效果。这要求程序员协同工作,当他编辑一 个文件的时候还要运行特定的脚本。 === 文件历史 === 因为Git记录的是项目范围的变更,重造单一文件的变更历史比其他跟踪单一文件的版本 控制系统要稍微麻烦些。 好在麻烦还不大,也是值得的,因为Git其他的操作难以置信地高效。例如,`git checkout`比`cp -a`都快,而且项目范围的delta压缩也比基于文件的delta集合的做法 好多了。 === 初始克隆 === 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. 当一个项目历史很长后,与在其他版本系统里的检出代码相比,创建一个克隆的开销会 大的多。 长远来看,开始付出的代价还是值得付出的,因为大多将来的操作将由此变得很快,并 可以离线完成。然而,在一些情况下,使用`--depth`创建一个浅克隆比较划算些。这种 克隆初始化的更快,但得到克隆的功能有所削减。 === 不稳定的项目 === 变更的大小决定写入的速度快慢是Git的设计。一般人做了小的改动就会提交新版本。这 里一行臭虫修改,那里一个新功能,修改掉的注释等等。但如果你的文件在相邻版本之 间存在极大的差异,那每次提交时,你的历史记录会以整个项目的大小增长。 任何版本控制系统对此都束手无策,但标准的Git用户将遭受更多,因为一般来说,历史 记录也会被克隆。 应该检查一下变更巨大的原因。或许文件格式需要改变一下。小修改应该仅仅导致几个 文件的细小改动。 或许,数据库或备份/打包方案才是正选,而不是版本控制系统。例如,版本控制就不适 宜用来管理网络摄像头周期性拍下的照片。 如果这些文件实在需要不断更改,他们实在需要版本控制,一个可能的办法是以中心的 方式使用Git。可以创建浅克隆,这样检出的较少,也没有项目的历史记录。当然,很多 Git工具就不能用了,并且修复必须以补丁的形式提交。这也许还不错,因为似乎没人需 要大幅度变化的不稳定文件历史。 另一个例子是基于固件的项目,使用巨大的二进制文件形式。用户对固件文件的变化历 史没有兴趣,更新的压缩比很低,因此固件修订将使仓库无谓的变大。 这种情况,源码应该保存在一个Git仓库里,二进制文件应该单独保存。为了简化问题, 应该发布一个脚本,使用Git克隆源码,对固件只做同步或Git浅克隆。 === 全局计数器 === 一些中心版本控制系统维护一个正整数,当一个新提交被接受的时候这个整数就增长。Git则是通过哈希值来记录所有变更,这在大多数情况下都工作的不错。 但一些人喜欢使用整数的方法。幸运的是,很容易就可以写个脚本,这样每次更新,中心Git仓库就增大这个整数,或使用tag的方式,把最新提交的哈希值与这个整数关联起来。 每个克隆都可以维护这么个计数器,但这或许没什么用,因为只有中心仓库以及它的计数器对每个人才有意义。 === 空子目录 === 空子目录不可加入管理。可以通过创建一个空文件以绕过这个问题。 Git的当前实现,而不是它的设计,是造成这个缺陷的原因。如果运气好,一旦Git得到 更多关注,更多用户要求这个功能,这个功能就会被实现。 === 初始提交 === 传统的计算机系统从0计数,而不是1。不幸的是,关于提交,Git并不遵从这一约定。很 多命令在初始提交之前都不友好。另外,一些极少数的情况必须作特别地处理。例如重 订一个使用不同初始提交的分支。 Git将从定义零提交中受益:一旦一个仓库被创建起来,HEAD将被设为包含20个零字节 的字符串。这个特别的提交代表一棵空的树,没有父节点,早于所有Git仓库。 然后运行git log,比如,通知用户至今还没有提交过变更,而不是报告致命错误并退出。 这与其他工具类似。 每个初始提交都隐式地成为这个零提交的后代。 不幸的是还有更糟糕的情况。如果把几个具有不同初始提交的分支合并到一起,之后的 重新修订不可避免的需要人员的介入。 === 接口怪癖 === 对提交A和提交B,表达式“A..B”和“A...B”的含义,取决于命令期望两个终点还是一 个范围。参见 *git help diff* 和 *git help rev-parse* 。 gitmagic-20160304/zh_cn/branch.txt0000644000175000017500000002457512666307504016114 0ustar sbadiasbadia== 分支巫术 == 即时分支合并是Git最给力的杀手锏。 *问题* :外部因素要求必须切换场景。在发布版本中突然蹦出个严重缺陷。某个特性完 成的截至日期就要来临。在项目关键部分可以提供帮助的一个开发正打算离职。所有情 况逼迫你停下所有手头工作,全力扑到到这个完全不同的任务上。 打断思维的连续性会使你的生产力大大降低,并且切换上下文也更麻烦,更大的损失。 使用中心版本控制我们必须从中心服务器下载一个新的工作拷贝。分布式系统的情况就 好多了,因为我们能够在本地克隆所需要的版本。 但是克隆仍然需要拷贝整个工作目录,还有直到给定点的整个历史记录。尽管Git使用文 件共享和硬链接减少了花费,项目文件自身还是必须在新的工作目录里重建。 *方案* :Git有一个更好的工具对付这种情况,比克隆快多了而且节省空间: *git branch* 。 使用这个魔咒,目录里的文件突然从一个版本变到另一个。除了只是在历史记录里上跳 下窜外,这个转换还可以做更多。你的文件可以从上一个发布版变到实验版本到当前开 发版本到你朋友的版本等等。 === 老板键 === 曾经玩过那样的游戏吗?按一个键(“老板键”),屏幕立即显示一个电子表格或别的? 那么如果老板走进办公室,而你正在玩游戏,就可以快速将游戏藏起来。 在某个目录: $ echo "I'm smarter than my boss" > myfile.txt $ git init $ git add . $ git commit -m "Initial commit" 我们已经创建了一个Git仓库,该仓库记录一个包含特定信息的文件。现在我们键入: $ git checkout -b boss # 之后似乎没啥变化 $ echo "My boss is smarter than me" > myfile.txt $ git commit -a -m "Another commit" 看起来我们刚刚只是覆盖了原来的文件并提交了它。但这是个错觉。键入: $ git checkout master # 切到文件的原先版本 嘿真快!这个文件就恢复了。并且如果老板决定窥视这个目录,键入: $ git checkout boss # 切到适合老板看的版本 你可以在两个版本之间相切多少次就切多少次,而且每个版本都可以独立提交。 === 肮脏的工作 === [[branch]] 比如你正在开发某个特性,并且由于某种原因,你需要回退三个版本,临时加进几行打 印语句来,来看看一些东西是如何工作的。那么: $ git commit -a $ git checkout HEAD~3 现在你可以到处加丑陋的临时代码。你甚至可以提交这些改动。当你做完的时候, $ git checkout master 来返回到你原来的工作。看,所有未提交变更都结转了。 如果你后来想保存临时变更怎么办?简单: $ git checkout -b dirty 只要在切换到主分支之前提交就可以了。无论你什么时候想回到脏的变更,只需键入: $ git checkout dirty 我们在前面章节讨论加载旧状态的时候,曾经接触过这个命令。最终我们把故事说全: 文件改变成请求的状态,但我们必须离开主分支。从现在开始的任何提交都会将你的文 件提交到另一条不同的路,这个路可以之后命名。 换一个说法,在checkout一个旧状态之后,Git自动把你放到一个新的,未命名的分支, 这个分支可以使用 *git checkout -b* 来命名和保存。 === 快速修订 === 你正在做某件事的当间,被告知先停所有的事情,去修理一个新近发现的臭虫,这个臭 虫在提交 `1b6d...`: $ git commit -a $ git checkout -b fixes 1b6d 那么一旦你修正了这个臭虫: $ git commit -a -m "Bug fixed" $ git checkout master 并可以继续你原来的任务。你甚至可以“合并”到最新修订: $ git merge fixes === 合并 === 一些版本控制系统,创建分支很容易,但把分支合并回来很难。使用Git,合并简直是家 常便饭,以至于甚至你可能对其发生没有察觉。 我们很久之前就遇到合并了。 *pull* 命令取出提交并合并它们到你的当前分支。如果 你没有本地变更,那这个合并就是一个“快进”,相当于中心式版本控制系统里的一个 弱化的获取最新版本操作。但如有本地变更,Git将自动合并,并报告任何冲突。 通常,一个提交只有一个“父提交”,也叫前一个提交。合并分支到一起产生一个至少 有两个父的提交。这就引出了问题: `HEAD~10` 真正指哪个提交?一个提交可能有多个 父,那我们跟哪个呢? 原来这个表示每次选择第一个父。这是可取的,因为在合并时候当前分支成了第一个父; 多数情况下我们只关注我们在当前分支都改了什么,而不是从其他分支合并来的变更。 你可以用插入符号来特别指定父。比如,显示来自第二个父的日志: $ git log HEAD^2 你可以忽略数字以指代第一个父。比如,显示与第一个父的差别: $ git diff HEAD^ 你可以结合其他类型使用这个记号。比如: $ git checkout 1b6d^^2~10 -b ancient 开始一个新分支 ``ancient'' ,表示第一个父的第二个父的倒数第十次提交的状态。 === 不间断工作流 === 经常在硬件项目里,计划的第二步必须等第一步完成才能开始。待修的汽车傻等在车库 里,直到特定的零件从工厂运来。一个原型在其可以构建之前,可能苦等芯片成型。 软件项目可能也类似。新功能的第二部分不得不等待,直到第一部分发布并通过测试。 一些项目要求你的代码需要审批才能接受,因此你可能需要等待第一部分得到批准,才 能开始第二部分。 多亏了无痛分支合并,我们可以不必遵循这些规则,在第一部分正式准备好前开始第二 部分的工作。假设你已经将第一部分提交并发去审批,比如说你现在在主分支。那么分 岔: $ git checkout -b part2 接下来,做第二部分,随时可以提交变更。只要是人就可能犯错误,经常你将回到第一 部分在修修补补。如果你非常幸运,或者超级棒,你可能不必做这几行: $ git checkout master # 回到第一部分 $ 修复问题 $ git commit -a # 提交变更 $ git checkout part2 # 回到第二部分 $ git merge master # 合并这些改动 最终,第一部分获得批准: $ git checkout master # 回到第一部分 $ submit files # 对世界发布 $ git merge part2 # 合并第二部分 $ git branch -d part2 # 删除分支“part2” 现在你再次处在主分支,第二部分的代码也在工作目录。 很容易扩展这个技巧,应用到任意数目的部分。它也很容易追溯分支:假如你很晚才意 识到你本应在7次提交前就创建分支。那么键入: $ git branch -m master part2 # 重命名“master”分支为“part2”。 $ git branch master HEAD~7 # 以七次前提交建一个新的“master”。 分支 `master` 只有第一部分内容,其他内容在分支 `part2` 。 我们现在后一个分支; 我们创建了 `master` 分支还没有切换过去,因为我们想继续工作在 `part2` 。这是不 寻常的。直到现在,我们已经在创建之后切换到分支,如: $ git checkout HEAD~7 -b master # 创建分支,并切换过去。 === 重组杂乱 === 或许你喜欢在同一个分支下完成工作的方方面面。你想为自己保留工作进度并希望其他 人只能看到你仔细整理过后的提交。开启一对分支: $ git branch sanitized # 为干净提交创建分支 $ git checkout -b medley # 创建并切换分支以进去工作 接下来,做任何事情:修臭虫,加特性,加临时代码,诸如此类,经常按这种方式提交。 然后: $ git checkout sanitized $ git cherry-pick medley^^ 应用分支 ``medley'' 的祖父提交到分支 ``sanitized'' 。通过合适的挑选(像选樱桃 那样)你可以构建一个只包含成熟代码的分支,而且相关的提交也组织在一起。 === 管理分支 === 列出所有分支: $ git branch 默认你从叫 ``master'' 的分支开始。一些人主张别碰“master”分支,而是创建你自 己版本的新分支。 选项 *-d* 和 *-m* 允许你来删除和移动(重命名)分支。参见 *git help branch* 。 分支``master'' 是一个有用的惯例。其他人可能假定你的仓库有一个叫这个名字的分 支,并且该分支包含你项目的官方版本。尽管你可以重命名或抹杀 ``master'' 分支, 你最好还是尊重这个约定。 === 临时分支 === 很快你会发现你经常会因为一些相似的原因创建短期的分支:每个其它分支只是为了保 存当前状态,那样你就可以直接跳到较老状态以修复高优先级的臭虫之类。 可以和电视的换台做类比,临时切到别的频道,来看看其它台那正放什么。但并不是简 单地按几个按钮,你不得不创建,检出,合并,以及删除临时分支。幸运的是,Git已经 有了和电视机遥控器一样方便的快捷方式: $ git stash 这个命令保存当前状态到一个临时的地方(一个隐藏的地方)并且恢复之前状态。你的 工作目录看起来和你开始编辑之前一样,并且你可以修复臭虫,引入之前变更等。当你 想回到隐藏状态的时候,键入: $ git stash apply # 你可能需要解决一些冲突 你可以有多个隐藏,并用不同的方式来操作他们。参见 *git help slash* 。也许你已 经猜到,Git维护在这个场景之后的分支以执行魔法技巧. === 按你希望的方式工作 === 你可能犹疑于分支是否值得一试。毕竟,克隆也几乎一样快,并且你可以用 *cd* 来在 彼此之间切换,而不是用Git深奥的命令。 考虑一下浏览器。为什么同时支持多标签和多窗口?因为允许两者同时接纳了多种风 格的用户。一些用户喜欢只保持一个打开的窗口,然后用标签浏览多个网页。一些可能 坚持另一个极端:任何地方都没有标签的多窗口。一些喜好处在两者之间。 分支类似你工作目录的标签,克隆类似打开的浏览器新窗口。这些是本地操作很快,那 为什么不试着找出最适合你的组合呢?Git让你按你确实所希望的那样工作。 gitmagic-20160304/zh_cn/secrets.txt0000644000175000017500000002656512666307504016330 0ustar sbadiasbadia== 揭开面纱 == 我们揭开Git神秘面纱,往里瞧瞧它是如何创造奇迹的。我会跳过细节。更深入的描述参 见 http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[ 用户手 册]。 === 大象无形 === Git怎么这么谦逊寡言呢?除了偶尔提交和合并外,你可以如常工作,就像不知道版本控 制系统存在一样。那就是,直到你需要它的时候,而且那是你欢欣的时候,Git一直默默 注视着你。 其他版本控制系统强迫你与繁文缛节和官僚主义不断斗争。文件的权限可能是只读的, 除非你显式地告诉中心服务器哪些文件你打算编辑。即使最基本的命令,随着用户数目 的增多,也会慢的像爬一样。中心服务器可能正跟踪什么人,什么时候check out了什么 代码。当网络连接断了的时候,你就遭殃了。开发人员不断地与这些版本控制系统的种 种限制作斗争。一旦网络或中心服务器瘫痪,工作就嘎然而止。 与之相反,Git简单地在你工作目录下的`.git`目录保存你项目的历史。这是你自己的历 史拷贝,因此你可以保持离线,直到你想和他人沟通为止。你拥有你的文件命运完全的 控制权,因为Git可以轻易在任何时候从`.git`重建一个曾经保存过的状态。 === 数据完整性 === 很多人把加密和保持信息机密关联起来,但一个同等重要的目标是保证信息安全。合理 使用哈希加密功能可以防止无意或有意的数据损坏行为。 一个SHA1哈希值可被认为是一个唯一的160位ID数,用它可以唯一标识你一生中遇到的每 个字节串。 实际上不止如此:每个字节串可供任何人用好多辈子。 对一个文件而言,其整体内容的哈希值可以被看作这个文件的唯一标识ID数。 因为一个SHA1哈希值本身也是一个字节串,我们可以哈希包括其他哈希值的字节串。这 个简单的观察出奇地有用:查看“哈希链”。我们之后会看Git如何利用这一点来高效地 保证数据完整性。 简言之,Git把数据保存在`.git/objects`子目录,那里看不到正常文件名,相反你只 看到ID。通过用ID作为文件名,加上一些文件锁和时间戳技巧,Git把任意一个原始的文 件系统转化为一个高效而稳定的数据库。 === 智能 === Git是如何知道你重命名了一个文件,即使你从来没有明确提及这个事实?当然,你或许 是运行了 *git mv* ,但这个命令和 *git add* 紧接 *git rm* 是完全一样的。 Git启发式地找出相连版本之间的重命名和拷贝。实际上,它能检测文件之间代码块的移 动或拷贝!尽管它不能覆盖所有的情况,但它已经做的很好了,并且这个功能也总在改 进中。如果它在你那儿不工作的话,可以尝试打开开销更高的拷贝检测选项,并考虑升 级。 === 索引 === 为每个加入管理的文件,Git在一个名为“index”的文件里记录统计信息,诸如大小, 创建时间和最后修改时间。为了确定文件是否更改,Git比较其当前统计信息与那些在索 引里的统计信息。如果一致,那Git就跳过重新读文件。 因为统计信息的调用比读文件内容快的很多,如果你仅仅编辑了少数几个文件,Git几乎 不需要什么时间就能更新他们的统计信息。 我们前面讲过索引是一个中转区。为什么一堆文件的统计数据是一个中转区?因为添加 命令将文件放到Git的数据库并更新它们的统计信息,而无参数的提交命令创建一个提交, 只基于这些统计信息和已经在数据库里的文件。 === Git的源起 === 这个 http://lkml.org/lkml/2005/4/6/121[ Linux内核邮件列表帖子] 描述了导致Git 的一系列事件。对Git史学家而言,整个讨论线索是一个令人着迷的历史探究过程。 === 对象数据库 === 你数据的每个版本都保存在“对象数据库”里,其位于子目录`.git/objects`;其他位 于`.git/`的较少数据:索引,分支名,标签,配置选项,日志,头提交的当前位置等。 对象数据库朴素而优雅,是Git的力量之源。 `.git/objects`里的每个文件是一个对象。有3种对象跟我们有关:“blob”对象, “tree”对象,和“commit”对象。 === Blob对象 === 首先来一个小把戏。选择一个文件名,任意文件名。在一个空目录: $ echo sweet > YOUR_FILENAME $ git init $ git add . $ find .git/objects -type f 你将看到 +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+ 。 我如何在不知道文件名的情况下知道这个?这是因为以下内容的SHA1哈希值: "blob" SP "6" NUL "sweet" LF 是 aa823728ea7d592acc69b36875a482cdf3fd5c8d,这里SP是一个空格,NUL是一个0字节, LF是一个换行符。你可以验证这一点,键入: $ printf "blob 6\000sweet\n" | sha1sum Git基于“内容寻址”:文件并不按它们的文件名存储,而是按它们包含内容的哈希值, 在一个叫“blob对象”的文件里。我们可以把文件内容的哈希值看作一个唯一ID,这样 在某种意义上我们通过他们内容放置文件。开始的“blob 6”只是一个包含对象类型与 其长度的头;它简化了内部存储。 这样我可以轻易预言你所看到的。文件名是无关的:只有里面的内容被用作构建blob对象。 你可能想知道对相同的文件会发生什么。试图加一个你文件的拷贝,什么文件名都行。 在 +.git/objects+ 的内容保持不变,不管你加了多少。Git只存储一次数据。 顺便说一句,在 +.git/objects+ 里的文件用zlib压缩,因此你不应该直接查看他们。 可以通过http://www.zlib.net/zpipe.c[zpipe -d] 管道, 或者键入: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d 这漂亮地打印出给定的对象。注意,上面的cat-file命令中,aa是目录名。 === Tree对象 === 但文件名在哪?它们必定在某个阶段保存在某个地方。Git在提交时得到文件名: $ git commit # 输入一些信息。 $ find .git/objects -type f 你应看到3个对象。这次我不能告诉你这两个新文件是什么,因为它部分依赖你选择的文 件名。我继续进行,假设你选了``rose''。如果你没有,你可以重写历史以让它看起来 像似你做了: $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose' $ find .git/objects -type f 现在你应看到文件 +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+ ,因为这是以下内容的SHA1哈希值: "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d 检查这个文件真的包含上面内容通过键入: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch 使用zpipe,验证哈希值是容易的: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum 与查看文件相比,哈希值验证更技巧一些,因为其输出不止包含原始未压缩文件。 这个文件是一个“tree”对象:一组数据包含文件类型,文件名和哈希值。在我们的例 子里,文件类型是100644,这意味着“rose”是一个一般文件,并且哈希值指blob对象, 包含“rose”的内容。其他可能文件类型有可执行,链接或者目录。在最后一个例子里, 哈希值指向一个tree对象。 在一些过渡性的分支,你会有一些你不再需要的老的对象,尽管在宽限过期之后,它们 会被自动清除,现在我们还是将其删除,以使我们比较容易跟上这个玩具例子。 $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune 在真实项目里你通常应该避免像这样的命令,因为你在破坏备份。如果你期望一个干净 的仓库,通常最好做一个新的克隆。还有,直接操作 +.git+ 时一定要小心:如果 Git命令同时也在运行会怎样,或者突然停电?一般,引用应由 *git update-ref -d* 删除,尽管通常手工删除 +refs/original+ 也是安全的。 === Commit对象 === 我们已经解释了三个对象中的两个。第三个是“commit”对象。其内容依赖于提交信息 以及其创建的日期和时间。为满足这里我们所有的,我们不得不调整一下: $ git commit --amend -m Shakespeare # 改提交信息 $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Rig timestamps and authors. $ find .git/objects -type f 你现在应看到 +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ 是下列 内容的SHA1哈希值: "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice 1234567890 -0800" LF "committer Bob 1234567890 -0800" LF LF "Shakespeare" LF 和前面一样,你可以运行zpipe或者cat-file来自己看。 这是第一个提交,因此没有父提交,但之后的提交将总有至少一行,指定一个父提交。 === 没那么神 === Git的秘密似乎太简单。看起来似乎你可以整合几个shell脚本,加几行C代码来弄起来, 也就几个小时的事:一个基本文件操作和SHA1哈希化的混杂,用锁文件装饰一下,文件 同步保证健壮性。实际上,这准确描述了Git的最早期版本。尽管如此,除了巧妙地打包 以节省空间,巧妙地索引以省时间,我们现在知道Git如何灵巧地改造文件系统成为一个 对版本控制完美的数据库。 例如,如果对象数据库里的任何一个文件由于硬盘错误损毁,那么其哈希值将不再匹配, 这个错误会报告给我们。通过哈希化其他对象的哈希值,我们在所有层面维护数据完整 性。Commit对象是原子的,也就是说,一个提交永远不会部分地记录变更:在我们已经 存储所有相关tree对象,blob对象和父commit对象之后,我们才可以计算提交的的哈希 值并将其存储在数据库,对象数据库不受诸如停电之类的意外中断影响。 我们打败即使是最狡猾的对手。假设有谁试图悄悄修改一个项目里一个远古版本文件的 内容。为使对象据库看起来健康,他们也必须修改相应blob对象的哈希值,既然它现在 是一个不同的字节串。这意味着他们讲不得不引用这个文件的tree对象的哈希值,并反 过来改变所有与这个tree相关的commit对象的哈希值,还要加上这些提交所有后裔的哈 希值。这暗示官方head的哈希值与这个坏仓库不同。通过跟踪不匹配哈希值线索,我 们可以查明残缺文件,以及第一个被破坏的提交。 总之,只要20个字节代表最后一次提交的是安全的,不可能篡改一个Git仓库。 那么Git的著名功能怎样呢?分支?合并?标签?单纯的细节。当前head保存在文件 +.git /HEAD+ ,其中包含了一个commit对象的哈希值。该哈希值在运行提交以及其他命 令时更新。分支几乎一样:它们是保存在 +.git/refs/heads+ 的文件。标签也是:它们 住在 +.git/refs/tags+ ,但它们由一套不同的命令更新。 gitmagic-20160304/zh_cn/multiplayer.txt0000644000175000017500000002013312666307504017210 0ustar sbadiasbadia== 多人Git == 我最初在一个私人项目上使用Git,那里我是唯一的开发。在与Git分布式本性有关的命 令中,我只用到了 *pull* 和 *clone*,用以在不同地方保持同一个项目。 后来我想用Git发布我的代码,并且包括其他贡献者的变更。我不得不学习如何管理有来 自世界各地的多个开发的项目,幸运的是,这是Git的长处,也可以说是其存在的理由。 === 我是谁? === 每个提交都有一个作者姓名和电子信箱,这显示在 *git log* 里。默认, Git使用系统 设定来填充这些域。要显示地设定,键入: $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com 去掉global选项设定只对当前仓库生效。 === Git在SSH, HTTP上 === 假设你有ssh访问权限,以访问一个网页服务器,但上面并没有安装Git。尽管比着它的 原生协议效率低,Git也是可以通过HTTP来进行通信的。 那么在你的帐户下,下载,编译并安装Git。在你的网页目录里创建一个Git仓库: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update 对较老版本的Git,只拷贝还不够,你应运行: $ chmod a+x hooks/post-update 现在你可以通过SSH从随便哪个克隆发布你的最新版本: $ git push web.server:/path/to/proj.git master 那随便谁都可以通过如下命令得到你的项目: $ git clone http://web.server/proj.git === Git在随便什么上 === 想无需服务器,甚至无需网络连接的时候同步仓库?需要在紧急时期凑合一下?我们 已经看过<>。 我们可以来来会会传送这些文件以传输git仓库, 通过任何媒介,但一个更有效率的工具是 *git bundle* 。 发送者创建一个“文件包”: $ git bundle create somefile HEAD 然后传输这个文件包, +somefile+ ,给某个其他参与者:电子邮件,优盘,一个 *xxd* 打印品和一个OCR扫描仪,通过电话读字节,狼烟,等等。接收者通过键入如下命 令从文件包获取提交: $ git pull somefile 接收者甚至可以在一个空仓库做这个。不考虑大小, +somefile+ 可以包含整个原先 git仓库。 在较大的项目里,可以通过只打包其他仓库缺少的变更消除浪费。例如,假设提交 ``1b6d...''是两个参与者共享的最近提交: $ git bundle create somefile HEAD ^1b6d 如果做的频繁,人可能容易忘记刚发了哪个提交。帮助页面建议使用标签解决这个问题。 即,在你发了一个文件包后,键入: $ git tag -f lastbundle HEAD 并创建较新文件包,使用: $ git bundle create newbundle HEAD ^lastbundle === 补丁:全球货币 === 补丁是变更的文本形式,易于计算机理解,人也类似。补丁可以通吃。你可以给开发电 邮一个补丁,不用管他们用的什么版本控制系统。只要你的观众可以读电子邮件,他们 就能看到你的修改。类似,在你这边,你只需要一个电子邮件帐号:不必搭建一个在线 的Git仓库。 回想一下第一章: $ git diff 1b6d > my.patch 输出是一个补丁,可以粘贴到电子邮件里用以讨论。在一个Git仓库,键入: $ git apply < my.patch 来打这个补丁。 在更正式些的设置里,当作者名字以及或许签名应该记录下的时候,为过去某一刻生成 补丁,键入: $ git format-patch 1b6d 结果文件可以给 *git-send-email* 发送,或者手工发送。你也可以指定一个提交范围: $ git format-patch 1b6d..HEAD^^ 在接收一端,保存邮件到一个文件,然后键入: $ git am < email.txt 这就打了补丁并创建了一个提交,包含诸如作者之类的信息。 使用浏览器邮件客户端,在保存补丁为文件之前,你可能需要建一个按钮,看看邮件内 容原来的原始形式。 对基于mbox的邮件客户端有些微不同,但如果你在使用的话,你可能是那种能轻易找出 答案的那种人,不用读教程。 === 对不起,移走了 === 克隆一个仓库后,运行 *git push* 或 *git pull* 讲自动推到或从原先URL拉。Git 如何做这个呢?秘密在和克隆一起创建的配置选项。让我们看一下: $ git config --list 选项 +remote.origin.url+ 控制URL源;``origin'' 是给源仓库的昵称。和 ``master'' 分支的惯例一样,我们可以改变或删除这个昵称,但通常没有理由这么做。 如果原先仓库移走,我们可以更新URL,通过: $ git config remote.origin.url git://new.url/proj.git 选项 +branch.master.merge+ 指定 *git pull* 里的默认远端分支。在初始克隆的时候, 它被设为原仓库的当前分支,因此即使原仓库之后挪到一个不同的分支,后来的 pull也将忠实地跟着原来的分支。 这个选项只使用我们初次克隆的仓库,它的值记录在选项 +branch.master.remote+ 。如果我们从其他仓库拉入,我们必须显示指定我们想要哪个分支: $ git pull git://example.com/other.git master 以上也解释了为什么我们较早一些push和pull的例子没有参数。 === 远端分支 === 当你克隆一个仓库,你也克隆了它的所有分支。你或许没有注意到因为Git将它们隐藏 起来了:你必须明确地要求。这使得远端仓库里的分支不至于干扰你的分支,也使 Git对初学者稍稍容易些。 列出远端分支,使用: $ git branch -r 你应该看到类似: origin/HEAD origin/master origin/experimental 这显示了远端仓库的分支和HEAD,可以用在常用的Git命令里。例如,假设你已经做了 很多提交,并希望和最后取到的版本比较一下。你可以搜索适当的SHA1哈希值,但使用 下面命令更容易些: $ git diff origin/HEAD 或你可以看看``experimental''分支都有啥: $ git log origin/experimental === 多远端 === 假设另两个开发在同一个项目上工作,我们希望保持两个标签。我们可以同时跟多个仓库: $ git remote add other git://example.com/some_repo.git $ git pull other some_branch 现在我们已经从第二个仓库合并到一个分支,并且我们已容易访问所有仓库的所有 分支。 $ git diff origin/experimental^ other/some_branch~5 但如果为了不影响自己的工作,我们只想比较他们的变更怎么办呢?换句话说,我们想 检查一下他们的分支,又不使他们的变更入侵我们的工作目录。那不是运行pull命令, 而是运行: $ git fetch # Fetch from origin, the default. $ git fetch other # Fetch from the second programmer. 这只是获取历史。尽管工作目录维持不变,我们可以参考任何仓库的任何分支,使用 一个Git命令,因为我们现在有一个本地拷贝。 回想一下,在幕后,一个pull是简单地一个 *fetch* 然后 *merge* 。通常,我们 *pull* 因为我们想在获取后合并最近提交;这个情况是一个值得注意的例外。 关于如何去除远端仓库,如何忽略特定分支等更多,参见 *git help remote* 。 === 我的喜好 === 对我手头的项目,我喜欢贡献者去准备仓库,这样我可以从其中拉。一些Git伺服让你 点一个按钮,拥有自己的分叉项目。 在我获取一个树之后,我运行Git命令去浏览并检查这些变更,理想情况下这些变更组织 良好,描述良好。我合并这些变更,也或许做些编辑。直到满意,我才把变更推入主资 源库。 尽管我不经常收到贡献,我相信这个方法扩展性良好。参见 http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[ 这篇 来自Linus Torvalds的博客 ] 呆在Git的世界里比补丁文件稍更方便,因为不用我将补丁转换到Git提交。更进一步, Git处理诸如作者姓名和信箱地址的细节,还有时间和日期,以及要求作者描述他们的提 交。 gitmagic-20160304/zh_cn/preface.txt0000644000175000017500000000710112666307504016246 0ustar sbadiasbadia= Git 魔法 = Ben Lynn 2007年8月 == 前言 == http://git.or.cz/[Git] 堪称版本控制瑞士军刀。这个可靠、多才多艺、用途多样的校 订工具异常灵活,以致不易掌握,更别说精通了。 正如Arthur C. Clarke所说,足够先进的技术与魔法无二。这是学习Git的好办法:新手 不妨忽略Git的内部机理,只当小把戏玩,借助Git其奇妙的能力,逗逗朋友,气气敌人。 为了不陷入细节,我们对特定功能提供大面上的讲解。在反复应用之后,慢慢地你会理 解每个小技巧如何工作,以及如何组合这些技巧以满足你的需求。 .翻译 - link:/\~blynn/gitmagic/intl/zh_cn/[简体中文]: 俊杰,萌和江薇。 link:/~blynn/gitmagic/intl/zh_tw/[正体中文] 由 + cconv -f UTF8-CN -t UTF8-TW + 转换。 - link:/~blynn/gitmagic/intl/fr/[法文]: Alexandre Garel。也在 http://tutoriels.itaapy.com/[itaapy]。 - link:/~blynn/gitmagic/intl/de/[德文]: Benjamin Bellee和Armin Stebich;也在 http://gitmagic.lordofbikes.de/[Armin的网站]。 - http://www.slideshare.net/slide_user/magia-git[葡萄牙文]: Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[ODT版]]。 - link:/~blynn/gitmagic/intl/ru/[俄文]: Tikhon Tarnavsky, Mikhail Dymskov, 和其他人。 - link:/~blynn/gitmagic/intl/es/[西班牙]: Rodrigo Toledo和Ariset Llerena Tapia。 - link:/~blynn/gitmagic/intl/vi/[越南文]: Trần Ngọc Quân; 也在 http://vnwildman.users.sourceforge.net/gitmagic.html[他的网站]. .其它版本 - link:book.html[单一文件]: 纯HTML,无CSS。 - link:book.pdf[PDF文件]: 打印效果好. - http://packages.debian.org/gitmagic[Debian包], http:://packages.ubuntu.com/gitmagic[Ubuntu包]: 本站快速本地拷贝。如果 http://csdcf.stanford.edu/status/[下线了]会方便些。 - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[纸质书 [Amazon.com]]: 64 页, 15.24cm x 22.86cm, 黑白。 没有电子设备的时候会方便些。 === 致谢! === 那么多人对本文档的翻译让我受宠若惊。他们的付出拓宽了读者群,我非常感激。 Dustin Sallings、Alberto Bertogli、James Cameron、Douglas Livingstone、 Michael Budde、Richard Albury、Tarmigan、 Derek Mahar、Frode Aannevik、 Keith Rarick、 Andy Somerville、 Ralf Recker、 Øyvind A. Holm、 Miklos Vajna、 Sébastien Hinderer、 Thomas Miedema、 Joe Malin、 和Tyler Breisacher对本文档 正确性和优化做出了贡献。 François Marier维护Debian包,该Debian包起初由Daniel Baumann创建。 感谢其他很多提供帮助和鼓励的人。名单太长了我无法一一写下。 如果我不小心把你的名字落下,请告诉我或者发一个补丁。 .免费Git伺服 - http://repo.or.cz/[http://repo.or.cz/] 为自由项目提供服务。第一个Git服务器。 由最早的Git开发人员创建和维护。 - http://gitorious.org/[http://gitorious.org/] 是另一个Git服务站点,为开源项 目提供服务。 - http://github.com/[http://github.com/] 开源项目免费,私有项目收钱。 感谢以上站点为本文档提供伺服服务。 === 许可 === 本指南在http://www.gnu.org/licenses/gpl-3.0.html[ GNU通用公共许可协议版本3 ] 之下发布。很自然,源码保存在一个Git仓库里,可以通过以下命令获得源码: $ git clone git://repo.or.cz/gitmagic.git # 创建“gitmagic”目录. 或从以下镜像得到: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git gitmagic-20160304/zh_cn/basic.txt0000644000175000017500000001601412666307504015725 0ustar sbadiasbadia== 基本技巧 == 与其一头扎进Git命令的海洋中,不如来点基本的例子试试手。它们简单而且实用。实际 上,在开始使用Git的头几个月,我所用的从来没超出本章介绍的内容。 === 保存状态 === 要不来点猛的?在做之前,先为当前目录所有文件做个快照,使用: $ git init $ git add . $ git commit -m "My first backup" 现在如果你的编辑乱了套,恢复之前的版本: $ git reset --hard 再次保存状态: $ git commit -a -m "Another backup" === 添加、删除、重命名 === 以上命令将只跟踪你第一次运行 *git add* 命令时就已经存在的文件。如果要添加新文 件或子目录,你需要告诉Git: $ git add readme.txt Documentation 类似,如果你想让Git忘记某些文件: $ git rm kludge.h obsolete.c $ git rm -r incriminating/evidence/ 这些文件如果还没删除,Git删除它们。 重命名文件和先删除旧文件,再添加新文件的一样。也有一个快捷方式 *git mv* ,和 *mv* 命令的用法一样。例如: $ git mv bug.c feature.c === 进阶撤销/重做 === 有时候你只想把某个时间点之后的所有改动都回滚掉,因为这些的改动是不正确的。那 么: $ git log 来显示最近提交列表,以及他们的SHA1哈希值: ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob Date: Tue Mar 14 01:59:26 2000 -0800 Replace printf() with write(). commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alice Date: Thu Jan 1 00:00:00 1970 +0000 Initial commit. ---------------------------------- 哈希值的前几个字符足够确定一个提交;也可以拷贝粘贴完整的哈希值,键入: $ git reset --hard 766f 来恢复到一个指定的提交状态,并从记录里永久抹掉所有比该记录新一些的提交。 另一些时候你想简单地跳到一个旧状态。这种情况,键入: $ git checkout 82f5 这个操作将把你带回过去,同时也保留较新提交。然而,像科幻电影里时光旅行一样, 如果你这时编辑并提交的话,你将身处另一个现实里,因为你的动作与开始时相比是不 同的。 这另一个现实叫作“分支”(branch),之后 <>。 至于现在,只要记住: $ git checkout master 会把你带到当下来就可以了。另外,为避免Git的抱怨,应该在每次运行checkout之前提 交(commit)或重置(reset)你的改动。 还以电脑游戏作为类比: - *`git reset --hard`*: 加载一个旧记录并删除所有比之新的记录。 - *`git checkout`*: 加载一个旧记录,但如果你在这个记录上玩,游戏状态将偏离第 一轮的较新状态。你现在打的所有游戏记录会在你刚进入的、代表另一个真实的分支 里。<>。 你可以选择只恢复特定文件和目录,通过将其加在命令之后: $ git checkout 82f5 some.file another.file 小心,这种形式的 *checkout* 会不声不响地覆盖文件。为阻止意外发生,在运行任何 checkout命令之前做提交,尤其在初学Git的时候。通常,任何时候你觉得对运行某个命 令不放心,无论Git命令还是不是Git命令,就先运行一下 *git commit -a* 。 不喜欢拷贝站题哈希值?那就用: $ git checkout :/"My first b" 来跳到以特定字符串开头的提交。你也可以回到倒数第五个保存状态: $ git checkout master~5 === 撤销 === 在法庭上,事件可以从法庭记录里敲出来。同样,你可以检出特定提交以撤销。 $ git commit -a $ git revert 1b6d 讲撤销给定哈希值的提交。本撤销被记录为一个新的提交,你可以通过运行 *git log* 来确认这一点。 === 变更日志生成 === 一些项目要求生成变更日志http://en.wikipedia.org/wiki/Changelog[changelog]. 生 成一个,通过键入: $ git log > ChangeLog === 下载文件 === 得到一个由Git管理的项目的拷贝,通过键入: $ git clone git://server/path/to/files 例如,得到我用来创建该站的所有文件: $ git clone git://git.or.cz/gitmagic.git 我们很快会对 *clone* 命令谈的很多。 === 到最新 === 如果你已经使用 *git clone* 命令得到了一个项目的一份拷贝,你可以更新到最新版, 通过: $ git pull === 快速发布 === 假设你写了一个脚本,想和他人分享。你可以只告诉他们从你的计算机下载,但如果此 时你正在改进你的脚本,或加入试验性质的改动,他们下载了你的脚本,他们可能由此 陷入困境。当然,这就是发布周期存在的原因。开发人员可能频繁进行项目修改,但他 们只在他们觉得代码可以见人的时候才择时发布。 用Git来完成这项,需要进入你的脚本所在目录: $ git init $ git add . $ git commit -m "First release" 然后告诉你的用户去运行: $ git clone your.computer:/path/to/script 来下载你的脚本。这要假定他们有ssh访问权限。如果没有,需要运行 *git daemon* 并 告诉你的用户去运行: $ git clone git://your.computer/path/to/script 从现在开始,每次你的脚本准备好发布时,就运行: $ git commit -a -m "Next release" 并且你的用户可以通过进入包含你脚本的目录,并键入下列命令,来更新他们的版本: $ git pull 你的用户永远也不会取到你不想让他们看到的脚本版本。显然这个技巧对所有的东西都 是可以,不仅是对脚本。 === 我们已经做了什么? === 找出自从上次提交之后你已经做了什么改变: $ git diff 或者自昨天的改变: $ git diff "@{yesterday}" 或者一个特定版本与倒数第二个变更之间: $ git diff 1b6d "master~2" 输出结果都是补丁格式,可以用 *git apply* 来把补丁打上。也可以试一下: $ git whatchanged --since="2 weeks ago" 我也经常用http://sourceforge.net/projects/qgit[qgit] 浏览历史, 因为他的图形界 面很养眼,或者 http://jonas.nitro.dk/tig/[tig] ,一个文本界面的东西,很慢的网 络状况下也工作的很好。也可以安装web 服务器,运行 *git instaweb* ,就可以用任 何浏览器浏览了。 === 练习 === 比方A,B,C,D是四个连续的提交,其中B与A一样,除了一些文件删除了。我们想把这 些删除的文件加回D。我们如何做到这个呢? 至少有三个解决方案。假设我们在D: 1. A与B的差别是那些删除的文件。我们可以创建一个补丁代表这些差别,然后吧补丁 打上: $ git diff B A | git apply 2. 既然这些文件存在A,我们可以把它们拿出来: $ git checkout A foo.c bar.h 3. 我们可以把从A到B的变化视为可撤销的变更: $ git revert B 哪个选择最好?这取决于你的喜好。利用Git满足自己需求是容易,经常还有多个方法。 gitmagic-20160304/zh_cn/grandmaster.txt0000644000175000017500000002222512666307504017154 0ustar sbadiasbadia== Git大师技 == 到现在,你应该有能力查阅 *git help* 页,并理解几乎所有东西。然而,查明解决特 定问题需要的确切命令可能是乏味的。或许我可以省你点功夫:以下是我过去曾经需要 的一些食谱。 === 源码发布 === 就我的项目而言,Git完全跟踪了我想打包并发布给用户的文件。创建一个源码包,我运 行: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === 提交变更 === 对特定项目而言,告诉Git你增加,删除和重命名了一些文件很麻烦。而键入如下命令会容易的多: $ git add . $ git add -u Git将查找当前目录的文件并自己算出具体的情况。除了用第二个add命令,如果你也打 算这时提交,可以运行`git commit -a`。关于如何指定应被忽略的文件,参见 *git help ignore* 。 你也可以用一行命令完成以上任务: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove 这里 *-z* 和 *-0* 选项可以消除包含特殊字符的文件名引起的不良副作用。注意这个 命令也添加应被忽略的文件,这时你可能需要加上 `-x` 或 `-X` 选项。 === 我的提交太大了! === 是不是忽视提交太久了?痴迷地编码,直到现在才想起有源码控制工具这回事?提交一 系列不相关的变更,因为那是你的风格? 别担心,运行: $ git add -p 为你做的每次修改,Git将展示给你变动的代码,并询问该变动是否应是下一次提交的一 部分。回答“y”或者“n”。也有其他选项,比如延迟决定;键入“?”来学习更多。 一旦你满意,键入 $ git commit 来精确地提交你所选择的变更(阶段变更)。确信你没加上 *-a* 选项,否则Git将提交 所有修改。 如果你修改了许多地方的许多文件怎么办?一个一个地查看变更令人沮丧,心态麻木。 这种情况下,使用 *git add -i* , 它的界面不是很直观,但更灵活。敲几个键,你可 以一次决定阶段或非阶段性提交几个文件,或查看并只选择特定文件的变更。作为另一 种选择,你还可以运行 *git commit --interactive* ,这个命令会在你操作完后自动 进行提交。 === 索引:Git的中转区域 === 当目前为止,我们已经忽略Git著名的'索引‘概念,但现在我们必须面对它,以解释上 面发生的。索引是一个临时中转区。Git很少在你的项目和它的历史之间直接倒腾数据。 通常,Git先写数据到索引,然后拷贝索引中的数据到最终目的地。 例如, *commit -a* 实际上是一个两步过程。第一步把每个追踪文件当前状态的快照放 到索引中。第二步永久记录索引中的快照。 没有 *-a* 的提交只执行第二步,并且只在 运行不知何故改变索引的命令才有意义,比如 *git add* 。 通常我们可以忽略索引并假装从历史中直接读并直接写。在这个情况下,我们希望更好 地控制,因此我们操作索引。我们放我们变更的一些的快照到索引中,而不是所有的, 然后永久地记录这个小心操纵的快照。 === 别丢了你的HEAD === HEAD好似一个游标,通常指向最新提交,随最新提交向前移动。一些Git命令让你来移动 它。 例如: $ git reset HEAD~3 将立即向回移动HEAD三个提交。这样所有Git命令都表现得好似你没有做那最后三个提交, 然而你的文件保持在现在的状态。具体应用参见帮助页。 但如何回到将来呢?过去的提交对将来一无所知。 如果你有原先Head的SHA1值,那么: $ git reset 1b6d 但假设你从来没有记下呢?别担心,像这些命令,Git保存原先的Head为一个叫 ORIG_HEAD的标记,你可以安全体面的返回: $ git reset ORIG_HEAD === HEAD捕猎 === 或许ORIG_HEAD不够。或许你刚认识到你犯了个历史性的错误,你需要回到一个早已忘记 分支上一个远古的提交。 默认,Git保存一个提交至少两星期,即使你命令Git摧毁该提交所在的分支。难点是找 到相应的哈希值。你可以查看在.git/objects里所有的哈希值并尝试找到你期望的。但 有一个更容易的办法。 Git把算出的提交哈希值记录在“.git/logs”。这个子目录引用包括所有分支上所有活 动的历史,同时文件HEAD显示它曾经有过的所有哈希值。后者可用来发现分支上一些不 小心丢掉提交的哈希值。 命令reflog为访问这些日志文件提供友好的接口,试试 $ git reflog 而不是从reflog拷贝粘贴哈希值,试一下: $ git checkout "@{10 minutes ago}" 或者捡出后五次访问过的提交,通过: $ git checkout "@{5}" 更多内容参见 *git help rev-parse* 的``Specifying Revisions''部分。 你或许期望去为已删除的提交设置一个更长的保存周期。例如: $ git config gc.pruneexpire "30 days" 意思是一个被删除的提交会在删除30天后,且运行 *git gc* 以后,被永久丢弃。 你或许还想关掉 *git gc* 的自动运行: $ git config gc.auto 0 在这种情况下提交将只在你手工运行 *git gc* 的情况下才永久删除。 === 基于Git构建 === 依照真正的UNIX风格设计,Git允许其易于用作其他程序的底层组件,比如图形界面, Web界面,可选择的命令行界面,补丁管理工具,导入和转换工具等等。实际上,一些 Git命令它们自己就是站在巨人肩膀上的脚本。通过一点修补,你可以定制Git适应你的 偏好。 一个简单的技巧是,用Git内建alias命令来缩短你最常使用命令: $ git config --global alias.co checkout $ git config --global --get-regexp alias # 显示当前别名 alias.co checkout $ git co foo # 和“git checkout foo”一样 另一个技巧,在提示符或窗口标题上打印当前分支。调用: $ git symbolic-ref HEAD 显示当前分支名。在实际应用中,你可能最想去掉“refs/heads/”并忽略错误: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- 子目录 +contrib+ 是一个基于Git工具的宝库。它们中的一些时时会被提升为官方命令。 在Debian和Ubuntu,这个目录位于 +/usr/share/doc/git-core/contrib+ 。 一个受欢迎的居民是 +workdir/git-new-workdir+ 。通过聪明的符号链接,这个脚本创 建一个新的工作目录,其历史与原来的仓库共享: $ git-new-workdir an/existing/repo new/directory 这个新的目录和其中的文件可被视为一个克隆,除了既然历史是共享的,两者的树自动 保持同步。不必合并,推入或拉出。 === 大胆的特技 === 这些天,Git使得用户意外摧毁数据变得更困难。但如若你知道你在做什么,你可以突破 为通用命令所设的防卫保障。 *Checkout*:未提交的变更会导致捡出失败。销毁你的变更,并无论如何都checkout一 个指定的提交,使用强制标记: $ git checkout -f HEAD^ 另外,如果你为捡出指定特别路径,那就没有安全检查了。提供的路径将被不加提示地 覆盖。如你使用这种方式的检出,要小心。 *Reset*: 如有未提交变更重置也会失败。强制其通过,运行: $ git reset --hard 1b6d *Branch*: 引起变更丢失的分支删除会失败。强制删除,键入: $ git branch -D dead_branch # instead of -d 类似,通过移动试图覆盖分支,如果随之而来有数据丢失,也会失败。强制移动分支,键入: $ git branch -M source target # 而不是 -m 不像checkout和重置,这两个命令延迟数据销毁。这个变更仍然存储在.git的子目录里, 并且可以通过恢复.git/logs里的相应哈希值获取(参见上面 上面“HEAD猎捕”)。默 认情况下,这些数据会保存至少两星期。 *Clean*: 一些Git命令拒绝执行,因为它们担心会重装未纳入管理的文件。如果你确信 所有未纳入管理的文件都是消耗,那就无情地删除它们,使用: $ git clean -f -d 下次,那个讨厌的命令就会工作! === 阻止坏提交 === 愚蠢的错误污染我的仓库。最可怕的是由于忘记 *git add* 而引起的文件丢失。较小 的罪过是行末追加空格并引起合并冲突:尽管危害少,我希望这些永远不要出现在公开 记录里。 不过我购买了傻瓜保险,通过使用一个_钩子_来提醒我这些问题: $ cd .git/hooks $ cp pre-commit.sample pre-commit # 对旧版本Git,先运行chmod +x 现在Git放弃提交,如果检测到无用的空格或未解决的合并冲突。 对本文档,我最终添加以下到 *pre-commit* 钩子的前面,来防止缺魂儿的事: if git ls-files -o | grep '\.txt$'; then echo FAIL! Untracked .txt files. exit 1 fi 几个git操作支持钩子;参见 *git help hooks* 。我们早先激活了作为例子的 *post-update* 钩子,当讨论基于HTTP的Git的时候。无论head何时移动,这个钩子都会 运行。例子脚本post-update更新Git在基于Git并不知晓的传输协议,诸如HTTP,通讯时 所需的文件。 gitmagic-20160304/zh_cn/history.txt0000644000175000017500000002463212666307504016352 0ustar sbadiasbadia== 关于历史 == Git分布式本性使得历史可以轻易编辑。但你若篡改过去,需要小心:只重写你独自拥有 的那部分。正如民族间会无休止的争论谁犯下了什么暴行一样,如果在另一个人的克隆 里,历史版本与你的不同,当你们的树互操作时,你会遇到一致性方面的问题。 一些开发人员强烈地感觉历史应该永远不变,不好的部分也不变所有都不变。另一些觉 得代码树在向外发布之前,应该整得漂漂亮亮的。Git同时支持两者的观点。像克隆,分 支和合并一样,重写历史只是Git给你的另一强大功能,至于如何明智地使用它,那是你 的事了。 === 我认错 === 刚提交,但你期望你输入的是一条不同的信息?那么键入: $ git commit --amend 来改变上一条信息。意识到你还忘记了加一个文件?运行git add来加,然后运行上面的 命令。 希望在上次提交里包括多一点的改动?那么就做这些改动并运行: $ git commit --amend -a === 更复杂情况 === 假设上面的问题再糟糕十倍,比如在漫长的时间里我们提交了一堆,但你不太喜欢他们的 组织方式,而且一些提交信息需要重写。那么键入: $ git rebase -i HEAD~10 则最后的10个提交会出现在你喜爱的$EDITOR(即通过设置EDITOR环境变量决定使用哪个 文本编辑器)。一个例子: pick 5c6eb73 Added repo.or.cz link pick a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile 不像git log,列表中的提交是旧的在前,新的在后。在上面的列表中,5c6eb73是最老的 提交,100834是最新的提交。接下来可以在$EDITOR中: - 通过删除行来移去提交。 - 通过为行重新排序行来重新排序提交。 - 替换 `pick` 使用: * `edit` 标记一个提交需要修订。 * `reword` 改变日志信息。 * `squash` 将一个提交与其前一个合并。 * `fixup` 将一个提交与其前一个合并,并丢弃日志信息。 比如,将第二个提交的`pick`修改为`squash`: pick 5c6eb73 Added repo.or.cz link squash a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile 保存退出,则Git会把a311a64合并进5c6eb73。想象一下“挤压”动作,squash的作用就是 把一个提交“挤压”进前一个提交。 “挤压”的同时,Git合并了两个提交的日志信息供编辑。比较一下*fixup*命令,其直接 扔掉“挤压“后合并的日志信息。 如果你把一个提交标记为*edit*,Git会带你回到这个提交点,你可以修改当时的提交信息 ,甚至可以增加新的提交。修改完毕后执行: $ git rebase --continue 直到遇到下一个*edit*标记点,Git会回放所有遇到的提交。 也可以放弃修改: $ git rebase --abort 这样尽早提交,经常提交:你之后还可以用rebase来规整。 === 本地变更之后 === 你正在一个活跃的项目上工作。随着时间推移,你做了几个本地提交,然后你使用合并 与官方版本同步。在你准备好提交到中心分支之前,这个循环会重复几次。 但现在你本地Git克隆掺杂了你的改动和官方改动。你更期望在变更列表里,你所有的变 更能够连续。 这就是上面提到的 *git rebase* 所做的工作。在很多情况下你可以使用 *--onto* 标 记以避免交互。 另外参见 *git help rebase* 以获取这个让人惊奇的命令更详细的例子。你可以拆分提 交。你甚至可以重新组织一棵树的分支。 要小心的是,rebase指令非常强悍,对于复杂的rebase,最好首先使用*git clone*备份 一下。 === 重写历史 === 偶尔,你需要做一些代码控制,好比从正式的照片中去除一些人一样,需要从历史记录 里面彻底的抹掉他们。例如,假设我们要发布一个项目,但由于一些原因,项目中的某 个文件不能公开。或许我把我的信用卡号记录在了一个文本文件里,而我又意外的把它 加入到了这个项目中。仅仅删除这个文件是不够的,因为从别的提交记录中还是可以访 问到这个文件。因此我们必须从所有的提交记录中彻底删除这个文件。 $ git filter-branch --tree-filter 'rm top/secret/file' HEAD 参见 *git help filter-branch* ,那里讨论了这个例子并给出一个更快的方法。一般 地, *filter-branch* 允许你使用一个单一命令来大范围地更改历史。 此后,+.git/refs/original+目录描述操作之前的状态。检查命令filter-branch的确做 了你想要做的,然后删除此目录,如果你想运行多次filter-branch命令。 最后,用你修订过的版本替换你的项目克隆,如果你想之后和它们交互的话。 === 制造历史 === [[makinghistory]] 想把一个项目迁移到Git吗?如果这个项目是在用比较有名气的系统,那可以使用一些其 他人已经写好的脚本,把整个项目历史记录导出来放到Git里。 否则,查一下 *git fast-import* ,这个命令会从一个特定格式的文本读入,从头来创 建Git历史记录。通常可以用这个命令很快写一个脚本运行一次,一次迁移整个项目。 作为一个例子,粘贴以下所列到临时文件,比如/tmp/history: ---------------------------------- commit refs/heads/master committer Alice Thu, 01 Jan 1970 00:00:00 +0000 data < int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob Tue, 14 Mar 2000 01:59:26 -0800 data < int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- 之后从这个临时文件创建一个Git仓库,键入: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history 你可以从这个项目checkout出最新的版本,使用: $ git checkout master . 命令*git fast-export* 转换任意仓库到 *git fast-import* 格式,你可以研究其输 出来写导出程序, 也以可读格式传送仓库。的确,这些命令可以发送仓库文本文件 通过只接受文本的渠道。 === 哪儿错了? === 你刚刚发现程序里有一个功能出错了,而你十分确定几个月以前它运行的很正常。天啊! 这个臭虫是从哪里冒出来的?要是那时候能按照开发的内容进行过测试该多好啊。 现在说这个已经太晚了。然而,即使你过去经常提交变更,Git还是可以精确的找出问题所在: $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git从历史记录中检出一个中间的状态。在这个状态上测试功能,如果还是有问题: $ git bisect bad 如果可以工作了,则把"bad"替换成"good"。Git会再次帮你找到一个以确定的好版本和 坏版本之间的状态,通过这种方式缩小范围。经过一系列的迭代,这种二分搜索会帮你 找到导致这个错误的那次提交。一旦完成了问题定位的调查,你可以返回到原始状态, 键入: $ git bisect reset 不需要手工测试每一次改动,执行如下命令可以自动的完成上面的搜索: $ git bisect run my_script Git使用指定命令(通常是一个一次性的脚本)的返回值来决定一次改动是否是正确的: 命令退出时的代码0代表改动是正确的,125代表要跳过对这次改动的检查,1到127之间 的其他数值代表改动是错误的。返回负数将会中断整个bisect的检查。 你还能做更多的事情: 帮助文档解释了如何展示bisects, 检查或重放bisect的日志,并 可以通过排除对已知正确改动的检查,得到更好的搜索速度。 === 谁让事情变糟了? === 和其他许多版本控制系统一样,Git也有一个"blame"命令: $ git blame bug.c 这个命令可以标注出一个指定的文件里每一行内容的最后修改者,和最后修改时间。但 不像其他版本控制系统,Git的这个操作是在线下完成的,它只需要从本地磁盘读取信息。 === 个人经验 === 在一个中心版本控制系统里,历史的更改是一个困难的操作,并且只有管理员才有权这 么做。没有网络,克隆,分支和合并都没法做。像一些基本的操作如浏览历史,或提交 变更也是如此。在一些系统里,用户使用网络连接仅仅是为了查看他们自己的变更,或 打开文件进行编辑。 中心系统排斥离线工作,也需要更昂贵的网络设施,特别是当开发人员增多的时候。最 重要的是,所有操作都一定程度变慢,一般在用户避免使用那些能不用则不用的高级命 令时。在极端的情况下,即使是最基本的命令也会变慢。当用户必须运行缓慢的命令的 时候,由于工作流被打断,生产力降低。 我有这些的一手经验。Git是我使用的第一个版本控制系统。我很快学会适应了它,用了 它提供的许多功能。我简单地假设其他系统也是相似的:选择一个版本控制系统应该和 选择一个编辑器或浏览器没啥两样。 在我之后被迫使用中心系统的时候,我被震惊了。我那有些脆弱的网络没给Git带来大麻 烦,但是当它需要像本地硬盘一样稳定的时候,它使开发困难重重。另外,我发现我自 己有选择地避免特定的命令,以避免踏雷,这极大地影响了我,使我不能按照我喜欢的 方式工作。 当我不得不运行一个慢的命令的时候,这种等待极大地破坏了我思绪连续性。在等待服 务器通讯完成的时候,我选择做其他的事情以度过这段时光,比如查看邮件或写其他的 文档。当我返回我原先的工作场景的时候,这个命令早已结束,并且我还需要浪费时间 试图记起我之前正在做什么。人类不擅长场景间的切换。 还有一个有意思的大众悲剧效应:预料到网络拥挤,为了减少将来的等待时间,每个人 将比以往消费更多的带宽在各种操作上。共同的努力加剧了拥挤,这等于是鼓励个人下 次消费更多带宽以避免更长时间的等待。 gitmagic-20160304/zh_cn/translate.txt0000644000175000017500000000262312666307504016642 0ustar sbadiasbadia== 附录 B: 本指南的翻译 == 我推荐如下步骤翻译本指南,这样我的脚本就可以快速生成HTML和PDF版本,并且所有翻 译也可以共存于同一个仓库。 克隆源码,然后针对不同目标 http://www.iana.org/assignments/language-subtag-registry[语言的IETF tag]创建 一个目录。参见 http://www.w3.org/International/articles/language-tags/Overview.en.php[ W3C在 国际化方面的文章 ]。例如,英语是“en”,日语是“ja”,正体中文是“zh-Hant”。 然后在新建目录,翻译这些来自“en”目录的 +txt+ 文件。 例如,将本指南译为 http://en.wikipedia.org/wiki/Klingon_language[ 克林贡语 ], 你也许键入: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" 是克林贡语的IETF语言码。 $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # 翻译这个文件 对每个文件都一样。 打开Makefile文件,把语言码加入`TRANSLATIONS`变量,现在你可以时不时查看你的工 作: $ make tlh $ firefox book-tlh/index.html 经常提交你的变更,然后然我知道他们什么时候完成。GitHub.com提供一个便于fork “gitmatic”项目的界面,提交你的变更,然后告诉我去合并。 但请按照最适合你的方式做:例如,中文译者就使用 Google Docs。只要你的工作使更多人看到我的工作,我就高兴。 gitmagic-20160304/zh_cn/intro.txt0000644000175000017500000001352412666307504016002 0ustar sbadiasbadia== 入门 == 我将用类比方式来介绍版本控制的概念。更严谨的解释参见 http://en.wikipedia.org/wiki/Revision_control[维基百科版本修订控制条目]。 === 工作是玩 === 我从小就玩电脑游戏。相反,我只是在长大后才开始使用版本控制系统。我想我并不特 殊,并且,对比两者工作方式可使这些概念更易解释,也易于理解。 编写代码,或编辑文档和玩游戏差不多。在你做出了很多进展之后,你最好保存一下。 去做这个,会点击你所信任的编辑器保存按钮就好了。 但这将覆盖老版本。就像那些学校里玩的老游戏,只有一个存档:你确实可以保存,但 你不能回到更老的状态了。这真让人扫兴,因为那个状态可能恰好保存了这个游戏特别 有意思一关,说不定哪天你想再玩一下呢。或者更糟糕的,你当前的保存是个必败局, 这样你就不得不从头开始玩了。 === 版本控制 === 在编辑的时候,如果想保留旧版本,你可以将文件“另存为”一个不同的文件,或在保 存之前将文件拷贝到别处。你可能压缩这些文件以节省空间。这是一个初级的靠手工的 版本控制方式。游戏软件早就提高了这块,很多都提供多个基于时间戳的自动存档槽。 让我们看看稍稍复杂的情况。比如你有很多放在一起的文件,比如项目源码,或网站文 件。现在如你想保留旧版本,你不得不把整个目录存档。手工保存多个版本很不方便, 而且很快会耗费巨大。 在一些电脑游戏里,一个存档真的包含在一个充满文件的目录里。这些游戏为玩家屏蔽 了这些细节,并提供一个方便易用的界面来管理该目录的不同版本。 版本控制系统也没有两样。两者提供友好的界面,来管理目录里的东西。你可以频繁保 存,也可以之后加载任一保存。不像大多计算机游戏,版本控制系统通常精于节省存储 空间。一般情况如果两个版本间只有少数文件的变更,每个文件的变更也不大,那就只 存储差异的部分,而不是把全部拷贝的都保存下来,以节省存储空间。 === 分布控制 === 现在设想一个很难的游戏。太难打了,以至于世界各地很多骨灰级玩家决定组队,分享 他们游戏存档以攻克它。Speedrun们就是实际中的例子:在同一个游戏里,玩家们分别 攻克不同的等级,协同工作以创造惊人战绩。 你如何搭建一个系统,使得他们易于得到彼此的存档?并易于上载新的存档? 在过去,每个项目都使用中心式版本控制。某个服务器上放所有保存的游戏记录。其他 人就不用了。每个玩家在他们机器上最多保留几个游戏记录。当一个玩家想更新进度时 候,他们需要把最新进度从主服务器下载下来,玩一会儿,保存并上载到主服务器以供 其他人使用。 假如一个玩家由于某种原因,想得到一个较旧版本的游戏进度怎么样?或许当前保存的 游戏是一个注定的败局,因为某人在第三级忘记捡某个物品;他们希望能找到最近一个 可以完成的游戏记录。或者他们想比较两个旧版本间的差异,来估算某个特定玩家干了 多少活。 查看旧版本的理由有很多,但检查的办法都是一样的。他们必须去问中心服务器要那个 旧版本的记录。需要的旧版本越多,和服务器的交互就越多。 新一代的版本控制系统,Git就是其中之一,是分布式的,可以被认作广义上的中心式系 统。从主服务器下载时玩家会得到所有保存的记录,而不仅是最新版。这看起来他们好 像把中心服务器做了个镜像。 最初的克隆操作可能比较费时,特别当有很长历史的时,但从长远看这是值得的。一个 显而易见的好处是,当查看一个旧版本时,不再需要和中心服务器通讯了。 === 一个误区 === 一个很常见的错误观念是,分布式系统不适合需要官方中心仓库的项目。这与事实并不 相符。给谁照相也不会偷走他们的灵魂。类似地,克隆主仓库并不降低它的重要性。 一般来说,一个中心版本控制系统能做的任何事,一个良好设计的分布式系统都能做得 更好。网络资源总要比本地资源耗费更费。不过我们应该在稍后分析分布式方案的缺点, 这样人们才不会按照习惯做出错误的比较。 一个小项目或许只需要分布式系统提供的一小部分功能,但是,在项目很小的时候,应 该用规划不好的系统?就好比说,在计算较小数目的时候应该使用罗马数字? 而且,你的项目的增长可能会超出你最初的预期。从一开始就使用Git好似带着一把瑞士 军刀,尽管你很多时候只是用它来开开瓶盖。某天你迫切需要一把改锥,你就会庆幸你 所有的不单单是一个启瓶器。 === 合并冲突 === 对于这个话题,电脑游戏的类比显得不够用。那让我们再来看看文档编辑的情况吧。 假设Alice在文档开头插入一行,并且Bob在文档末尾添加一行。他们都上传了他们的改 动。大多数系统将自动给出一个合理的处理方式:接受且合并他们的改动,这样Alice和 Bob两人的改动都会生效。 现在假设Alice和Bob对文件的同一行做了不同的改动。如果没有人工参与的话,这个冲 突是无法解决的。第二个人在上载文件时,会收到 _合并冲突_ 的通知,要么用一个人 的改动覆盖另一个的,要么完全修订这一行。 更复杂的情况也可能出现。版本控制系统自己处理相对简单的情况,把困难的情况留给 人来处理。它们的行为通常是可配置的。 gitmagic-20160304/zh_cn/clone.txt0000644000175000017500000002267012666307504015751 0ustar sbadiasbadia== 克隆周边 == 在较老一代的版本控制系统里,checkout是获取文件的标准操作。你将获得一组特定保 存状态的文件。 在Git和其他分布式版本控制系统里,克隆是标准的操作。通过创建整个仓库的克隆来 获得文件。或者说,你实际上把整个中心服务器做了个镜像。凡是主仓库上能做的事, 你都能做。 === 计算机间同步 === 我可以忍受制作tar包或利用rsync来作备份和基本同步。但我有时在我笔记本上编辑, 其他时间在台式机上,而且这俩之间也许并不交互。 在一个机器上初始化一个Git仓库并提交你的文件。然后转到另一台机器上: $ git clone other.computer:/path/to/files 以创建这些文件和Git仓库的第二个拷贝。从现在开始, $ git commit -a $ git pull other.computer:/path/to/files HEAD 将把另一台机器上特定状态的文件“拉”到你正工作的机器上。如果你最近对同一个文 件做了有冲突的修改,Git将通知你,而你也应该在解决冲突之后再次提交。 === 典型源码控制 === 为你的文件初始化Git仓库: $ git init $ git add . $ git commit -m "Initial commit" 在中心服务器,在某个目录初始化一个“裸仓库”: $ mkdir proj.git $ cd proj.git $ git init --bare $ touch proj.git/git-daemon-export-ok 如需要的话,启动Git守护进程: $ git daemon --detach # 它也许已经在运行了 对一些Git伺服服务,按照其指导来初始化空Git仓库。一般是在网页上填一个表单。 把你的项目“推”到中心服务器: $ git push central.server/path/to/proj.git HEAD 捡出源码,可以键入: $ git clone central.server/path/to/proj.git 做了改动之后,开发保存变更到本地: $ git commit -a 更新到最近版本: $ git pull 所有冲突应被处理,然后提交: $ git commit -a 把本地改动捡入到中心仓库: $ git push 如果主服务器由于其他开发的活动,有了新的变更,这个捡入会失败,该开发应该把最 新版本拿下来,解决合并冲突,然后重试。 为使用上面pull和push命令,开发必须有SSH访问权限。不过,通过键入以下命令,任何 人都可以看到源码: $ git clone git://central.server/path/to/proj.git 本地git协议和HTTP类似:并无安全验证,因此任何人都能拿到项目。因此,默认情况 git协议禁止推操作。 === 封闭源码 === 闭源项目不要执行touch命令,并确保你从未创建`git-daemon-export-ok`文件。资源库 不再可以通过git协议获取;只有那些有SSH访问权限的人才能看到。如果你所有的资源 库都是封闭的,那也没必要运行运行git守护了,因为所有沟通都走SSH。 === 裸仓库 === 之所以叫裸仓库是因为其没有工作目录;它只包含正常情况下隐藏在`.git`子目录下 的文件。换句话说,它维护项目历史,而且从不保存任何给定版本的快照。 裸仓库扮演的角色和中心版本控制系统中中心服务器的角色类似:你项目的中心。开 发从其中克隆项目,捡入新近改动。典型地裸仓库存在一个服务器上,该服务器除了 分散数据外并不做啥。开发活动发生在克隆上,因此中心仓库没有工作目录也行。 很多Git命令在裸仓库上失败,除非指定仓库路径到环境变量`GIT_DIR`,或者指定 `--bare`选项。 === 推还是拽 === 为什么我们介绍了push命令,而不是依赖熟悉的pull命令?首先,在裸仓库上pull会 失败:除非你必须“fetch”,一个之后我们要讨论的命令。但即使我们在中心服务器上 保持一个正常的仓库,拽些东西进去仍然很繁琐。我们不得不登陆服务器先,给pull 命令我们要拽自机器的网络地址。防火墙会阻碍,并且首先如果我们没有到服务器的 shell访问怎么办呢? 然而,除了这个案例,我们反对推进仓库,因为当目标有工作目录时,困惑随之而来。 简短截说,学习Git的时候,只在目标是裸仓库的时候push,否则用pull的方式。 === 项目分叉 === 项目走歪了吗?或者认为你可以做得更好?那么在服务器上: $ git clone git://main.server/path/to/files 之后告诉每个相关的人你服务器上项目的分支。 在之后的时间,你可以合并来自原先项目的改变,使用命令: $ git pull === 终极备份 === 会有很多散布在各处,禁止篡改的冗余存档吗? 如果你的项目有很多开发,那干脆啥也 别做了。你的每份代码克隆是一个有效备份。不仅当前状态,还包括你项目整个历史。 感谢哈希加密算法,如果任何人的克隆被损坏,只要他们与其他的交互,这个克隆就会 被修好。 如果你的项目并不是那么流行,那就找尽可能多的伺服来放克隆吧。 真正的偏执狂应该总是把HEAD最近20字节的SHA1哈希值写到安全的地方。应该保证安全, 而不是把它藏起来。比如,把它发布到报纸上就不错,因为对攻击者而言,更改每份报 纸是很难的。 === 轻快多任务 === 比如你想并行开发多个功能。那么提交你的项目并运行: $ git clone . /some/new/directory Git使用硬链接和文件共享来尽可能安全地创建克隆,因此它一眨眼就完成了,因此你现 在可以并行操作两个没有相互依赖的功能。例如,你可以编辑一个克隆,同时编译另一 个。感谢 http://en.wikipedia.org/wiki/Hard_link[hardlinking], 本地克隆比简单 备份省时省地。 现在你可以同时工作在两个彼此独立的特性上。比如,你可以在编译一个克隆的时候编 辑另一个克隆。任何时候,你都可以从其它克隆提交并拖拽变更。 $ git pull /the/other/clone HEAD === 游击版本控制 === 你正做一个使用其他版本控制系统的项目, 而你非常思念Git? 那么在你的工作目录初 始化一个Git仓库: $ git init $ git add . $ git commit -m "Initial commit" 然后克隆它: $ git clone . /some/new/directory 并在这个目录工作,按你所想在使用Git。过一会,一旦你想和其他每个人同步,在这种 情况下,转到原来的目录,用其他的版本控制工具同步,并键入: $ git add . $ git commit -m "Sync with everyone else" 现在转到新目录运行: $ git commit -a -m "Description of my changes" $ git pull 把你的变更提交给他人的过程依赖于其他版本控制系统。这个新目录包含你的改动的文 件。需要运行其他版本控制系统的命令来上载这些变更到中心仓库。 Subversion, 或许是最好的中心式版本控制系统,为无数项目所用。 *git svn* 命令为 Subversion仓库自动化了上面的操作,并且也可以用作 http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[ 导出Git项目到Subversion仓库] 的替代。 === Mercurial === Mercurial是一个类似的的版本控制系统,几乎可以和Git一起无缝工作。使用 `hg-git`插件,一个Mercurial用户可以无损地往Git仓库推送,从Git仓库拖拽。 使用Git获得`hg-git`插件: $ git clone git://github.com/schacon/hg-git.git 或使用Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ 不好意思,我没注意Git有类似的插件。因此, 我主张使用Git而不是Mercurial作为主资 源库,即使你偏爱Mercurial。使用Mercurial项目,通常一个自愿者维护一个平行的 Git项目以适应Git用户,然而感谢`hg-git`插件,一个Git项目自动地适应Mercurial用 户。 尽管该插件可以把一个Mercurial仓库转成一个Git仓库,通过推到一个空的仓库, 这个差事交给`hg-fast-export.sh`脚本还是更容易些。来自: $ git clone git://repo.or.cz/fast-export.git 要转化,只需在一个空目录运行: $ git init $ hg-fast-export.sh -r /hg/repo 注意该脚本应加入你的`$PATH`。 === Bazaar === 我们简略提一下Bazaar,它毕竟是紧跟Git和Mercurial之后最流行的自由分布式版本控 制系统。 Bazaar有后来者的优势,它相对年轻些;它的设计者可以从前人的错误中学习,并且躲 过去翻历史上犯过的错误。另外,它的开发人员对可移植性以及和与其它版本控制系统 的互操作性也考虑周全。 一个`bzr-git`插件让Bazaar用户在一定程度下可以工作在Git仓库。`tailor`程序转 换Bazaar仓库到Git仓库,并且可以递增的方式做,要知道`bzr-fast-export`只是 在一次性转换性情况下工作良好。 === 我偏爱Git的原因 === 我起先选择Git是因为我听说它能管理不可想象地不可管理的Linux内核源码。我从来没 觉得有离开的必要。Git已经服侍的很好了,并且我也没有被其瑕疵所困扰。因为我主要 使用Linux,其他平台上的问题与我无关。 还有,我偏爱C程序和bash脚本,以及诸如Python的可执行可脚本:较少依赖,并且我也 沉迷于快速的执行时间。 我考虑过Git才能如何提高,甚至自己写类似的工具,但只作为研究练练手。即使完成这 个项目,我也无论如何会继续使用Git,因为使用一个古里古怪的系统所获甚微。 自然地,你的需求和期望可能不同,并且你可能使用另一个系统会好些。尽管如此,使 用Git你都错不太远。 gitmagic-20160304/ru/0000755000175000017500000000000012666307504013426 5ustar sbadiasbadiagitmagic-20160304/ru/drawbacks.txt0000644000175000017500000003056612666307504016142 0ustar sbadiasbadia== Приложение A: Недостатки Git == Есть некоторые проблемы Git, которые я спрятал под сукно. Некоторые из них можно легко решить с помощью скриптов и хуков, некоторые требуют реорганизации или пересмотра проекта, а несколько оставшихся неприятностей придется потерпеть. А еще лучше — взяться за них и решить! === Слабости SHA1 === Со временем криптографы обнаруживают всё больше и больше слабостей в SHA1. Уже сейчас обнаружение коллизий хешей осуществимо для хорошо финансируемой организации. Спустя годы, возможно, даже типичный ПК будет иметь достаточную вычислительную мощность, чтобы незаметно испортить хранилище Git. Надеюсь, Git перейдет на лучшую хеш-функцию прежде чем дальнейшие исследования уничтожат SHA1. === Microsoft Windows === Git на Microsoft Windows может быть громоздким: - http://cygwin.com/[Cygwin], Linux-подобная среда для Windows, содержащая http://cygwin.com/packages/git/[порт Git на Windows]. - http://code.google.com/p/msysgit/[Git на MSys], вариант, требующий минимальной рантайм поддержки, хотя некоторые комманды нуждаются в доработке. === Несвязанные файлы === Если ваш проект очень велик и содержит много несвязанных файлов, которые постоянно изменяются, Git может оказаться в невыгодном положении по сравнению с другими системами, поскольку отдельные файлы не отслеживаются. Git отслеживает изменения всего проекта, что обычно бывает выгодным. Решение — разбить проект на части, каждая из которых состоит из взаимосвязанных файлов. Используйте *git submodule* если вы все же хотите держать все в одном хранилище. === Кто и что редактировал ? === Некоторые системы управления версиями вынуждают вас явным образом пометить файл перед редактированием. Хотя такой подход особенно раздражает, когда подразумевает работу с центральным сервером, однако он имеет два преимущества: 1. Diff'ы быстры, так как нужно проверить только отмеченные файлы. 2. Можно обнаружить, кто еще работает с этим файлом, спросив центральный сервер, кто отметил его для редактирования. С помощью соответствующих скриптов, вы можете добиться того же с Git. Это требует сотрудничества со стороны другого программиста, который должен запускать определенный скрипт при редактировании файла. === История файла === Поскольку Git записывает изменения всего проекта, воссоздание истории единичного файла требует больше работы, чем в системах управления версиями, следящими за отдельными файлами. Потери как правило незначительны, и это неплохая цена за то, что другие операции невероятно эффективны. Например, git checkout быстрее, чем cp -a, а дельта всего проекта сжимается лучше, чем коллекция по-файловых дельт. === Начальное Клонирование === Создание клона хранилища дороже обычного чекаута в других системах управления версиями при длинной истории. Первоначальная цена окупается в долгосрочной перспективе, так как большинство последующих операций будут быстрыми и автономными. Однако в некоторых ситуациях может быть предпочтительным создание мелких клонов с опцией --depth. Это намного быстрее, но у полученного клона будет урезанная функциональность. === Изменчивые Проекты === Git был написан, чтобы быть быстрым при относительно небольших изменениях. Люди вносят незначительные правки от версии к версии. Однострочное исправление ошибки здесь, новая функция там, исправленные комментарии и тому подобное. Но если ваши файлы радикально различаются в соседних ревизиях, то с каждым коммитом ваша история неизбежно увеличится на размер всего проекта. Никакая система управления версиями ничего не может с этим сделать, но пользователи Git страдают больше, поскольку обычно истории клонируются. Причины, по которым эти изменения столь велики, нужно изучить. Возможно, надо изменить форматы файлов. Небольшие правки должны приводить к небольшим изменений не более чем в нескольких файлах. Возможно, вам была нужна база данных или система резервного/архивного копирования, а не система управления версиями. Например, управление версиями может быть плохо приспособлено для обращения с фотографиями периодически получаемыми с веб-камеры. Если файлы действительно должны постоянно изменяться и при этом версироваться, может иметь смысл использовать Git централизованным образом. Можно создавать мелкие клоны, с небольшой историей или без истории вообще. Конечно, многие инструменты Git будут недоступны, и исправления придется представлять в виде патчей. Возможно, это и хорошо, так как неясно, зачем кому-либо понадобится история крайне нестабильных файлов. Другой пример — это проект, зависимый от прошивки, принимающей форму огромного двоичного файла. Ее история неинтересна пользователям, а обновления плохо сжимаются, потому ревизии прошивки будут неоправдано раздувать размер хранилища. В этом случае исходный код стоит держать в хранилище Git, а бинарные файлы — отдельно. Для упрощения жизни можно распространять скрипт, использующий Git для клонирования кода и rsync или мелкий клон Git для прошивки. === Глобальный счетчик === Некоторые централизованные системы управления версиями содержат натуральное число, увеличивающееся при поступлении нового коммита. Git идентифицирует изменения по их хешам, что лучше во многих обстоятельствах. Но некоторым людям нравятся эти целые числа повсюду. К счастью, легко написать такой скрипт, чтобы при каждом обновлении центральное хранилище Git увеличивало целое число, возможно, в теге, и связывало его с хешем последнего коммита. Каждый клон может поддерживать такой счетчик, но это, видимо, будет бесполезным, поскольку только центральное хранилище и его счетчик имеет значение для всех. === Пустые подкаталоги === Пустые подкаталоги не могут отслеживаться. Создавайте подставные файлы, чтобы обойти эту проблему. В этом виноват не дизайн Git, а его текущая реализация. Если повезет и пользователи Git будут поднимать больше шума вокруг этой функции, возможно она будет реализована. === Первоначальный коммит === Шаблонный компьютерщик считает с 0, а не с 1. К сожалению, в отношении коммитов Git не придерживается этого соглашения. Многие команды недружелюбны до первоначального коммита. Кроме того, некоторые частные случаи требуют специальной обработки, к примеру rebase ветки с другим начальным коммитом. Git'у было бы выгодно определить нулевой коммит: при создании хранилища HEAD был бы установлен в строку, состоящую из 20 нулевых байтов. Этот специальный коммит представлял бы собой пустое дерево, без родителей, которое предшествует каждому хранилищу Git. Тогда запуск *git log*, например, показывал бы пользователю, что коммиты еще не были сделаны, вместо того чтобы завершаться с фатальной ошибкой. Аналогично для других инструментов. Каждый первоначальный коммит — неявный потомок этого нулевого коммита. Однако здесь, к сожалению, есть некоторые проблемные случаи. Если несколько ветвей с различными начальными коммитами сливаются, то rebase результата требует значительного ручного вмешательства. === Причуды интерфейса === Для коммитов А и Б значения выражений «А..Б» и «А...Б» зависят от того, ожидает ли команда указания двух конечных точек или промежутка. Смотрите *git help diff* и *git help rev-parse*. gitmagic-20160304/ru/branch.txt0000644000175000017500000005163312666307504015434 0ustar sbadiasbadia== Чудеса ветвления == Возможности мгновенного ветвления и слияния — самые замечательный особенности Git. *Задача*: внешние факторы неизбежно влекут переключение внимания. Серьезная ошибка в уже выпущенной версии обнаруживается без предупреждения. Срок сдачи определённой функциональности приближается. Разработчик, помощь которого нужна вам в работе над ключевой частью проекта, собирается в отпуск. Одним словом, вам нужно срочно бросить все, над чем вы трудитесь в настоящий момент, и переключиться на совершенно другие задачи. Прерывание хода ваших мыслей может серьезно снизить эффективность работы, и чем сложнее переключение между процессами, тем больше будет потеря. При централизованном управлении версиями мы вынуждены скачивать свежую рабочую копию с центрального сервера. Распределенная система лучше: мы можем клонировать нужную версию локально. Однако клонирование все же предполагает копирование всего рабочего каталога, как и всей истории изменений до настоящего момента. Хотя Git и снижает затратность этого действия за счет возможности совместного использования файлов и жестких ссылок, но все файлы проекта придется полностью воссоздать в новом рабочем каталоге. *Решение*: у Git есть более удобный инструмент для таких случаев, который сэкономит и время, и дисковое пространство по сравнению с клонированием — это *git branch* (branch — ветка, прим. пер.). Этим волшебным словом файлы в вашем каталоге мгновенно преобразуются от одной версии к другой. Это изменение позволяет сделать намного больше, чем просто вернуться назад или продвинуться вперед в истории. Ваши файлы могут изменится с последней выпущенной версии на экспериментальную, с экспериментальной — на текущую версию в разработке, с нее — на версию вашего друга и так далее. === Кнопка босса === Играли когда-нибудь в одну из таких игр, где при нажатии определеной клавиши («кнопки босса»), на экране мгновенно отображается таблица или что-то вроде того? То есть, если в офис зашел начальник, а вы играете в игру, вы можете быстро ее скрыть. В каком-нибудь каталоге: $ echo "Я хитрее моего босса" > myfile.txt $ git init $ git add . $ git commit -m "Начальный коммит" Мы создали хранилище Git, содержащее один текстовый файл с определенным сообщением. Теперь выполните $ git checkout -b boss # вероятно, это последнее изменение $ echo "Мой босс умнее меня" > myfile.txt $ git commit -a -m "Другой коммит" Это выглядит так, будто мы только что перезаписали файл и сделали коммит. Но это иллюзия. Наберите $ git checkout master # переключиться на оригинальную версию файла Вуаля! Текстовый файл восстановлен. А если босс решит сунуть нос в этот каталог, запустите $ git checkout boss # перейти на версию, подходящую для глаз босса Вы можете переключаться между двумя версиями этого файла так часто, как вам хочется и делать коммиты каждой из них независимо. === Грязная работа === [[branch]] Допустим, вы работаете над некой функцией, и вам зачем-то понадобилось вернуться на три версии назад и временно добавить несколько операторов вывода, чтобы посмотреть как что-либо работает. Тогда введите $ git commit -a $ git checkout HEAD~3 Теперь вы можете добавлять временный черновой код в любых местах. Можно даже закоммитить эти изменения. Когда закончите, выполните $ git checkout master чтобы вернуться к исходной работе. Заметьте, что любые изменения, не внесенные в коммит, будут перенесены. А что, если вы все-таки хотели сохранить временные изменения? Запросто: $ git checkout -b dirty а затем сделайте коммит перед возвращением в ветку master. Всякий раз, когда вы захотите вернуться к черновым изменениям, просто выполните $ git checkout dirty Мы говорили об этой команде в одной из предыдущих глав, когда обсуждали загрузку старых состояний. Теперь у нас перед глазами полная картина: файлы изменились к нужному состоянию, но мы должны покинуть главную ветку. Любые коммиты, сделанные с этого момента, направят файлы по другому пути, к которому можно будет вернуться позже. Другими словами, после переключения на более старое состояние Git автоматически направляет вас по новой безымянной ветке, которой можно дать имя и сохранить ее с помощью *git checkout -b*. === Быстрые исправления === Ваша работа в самом разгаре, когда вдруг выясняется, что нужно все бросить и исправить только что обнаруженную ошибку в коммите «1b6d…»: $ git commit -a $ git checkout -b fixes 1b6d После исправления ошибки сделайте $ git commit -a -m "Ошибка исправлена" $ git checkout master и вернитесь к работе над вашими исходными задачами. Вы можете даже «влить» только что сделанное исправление ошибки в основную ветку: $ git merge fixes === Слияния === В некоторых системах управления версиями создавать ветки легко, а вот сливать их воедино трудно. В Git слияние столь тривиально, что вы можете его не заметить. На самом деле мы сталкивались со слияниями уже давно. Команда *pull* по сути получает коммиты, а затем сливает их с вашей текущей веткой. Если у вас нет локальных изменений, слияние произойдет само собой, как вырожденный случай вроде получения последней версии в централизованной системе управления версиями. Если же у вас есть локальные изменения, Git автоматически произведет слияние и сообщит о любых конфликтах. Обычно у коммита есть один «родитель», а именно предыдущий коммит. Слияние веток приводит к коммиту как минимум с двумя родителями. Отсюда возникает вопрос: к какому коммиту на самом деле отсылает HEAD~10? Коммит может иметь несколько родителей, так за которым из них следовать далее? Оказывается, такая запись всегда выбирает первого родителя. Это хороший выбор, потому что текущая ветка становятся первым родителем во время слияния. Часто вас интересуют только изменения, сделанные вами в текущей ветке, а не те, которые влились из других веток. Вы можете обращаться к конкретному родителю с помощью символа «^». Например, чтобы показать запись в журнале от второго родителя, наберите $ git log HEAD^2 Для первого родителя номер можно опустить. Например, чтобы показать разницу с первым родителем, введите $ git diff HEAD^ Вы можете сочетать такую запись с другими. Например, $ git checkout 1b6d^^2~10 -b ancient создаст новую ветку «ancient» («древняя», прим. пер.), отражающую состояние на десять коммитов назад от второго родителя первого родителя коммита, начинающегося с 1b6d. === Непрерывный рабочий процесс === В производстве техники часто бывает, что второй шаг плана должен ждать завершения первого шага. Автомобиль, нуждающийся в ремонте, может тихо стоять в гараже до прибытия с завода конкретной детали. Прототип может ждать производства чипа, прежде чем разработка будет продолжена. И в разработке ПО может быть то же. Вторая порция новой функциональности может быть вынуждена ожидать выпуска и тестирования первой части. Некоторые проекты требуют проверки вашего кода перед его принятием, так что вы должны дождаться утверждения первой части, прежде чем начинать вторую. Благодаря безболезненным ветвлению и слиянию, мы можем изменить правила и работать над второй частью до того, как первая официально будет готова. Допустим, вы закоммитили первую часть и выслали ее на проверку. Скажем, вы в ветке master. Теперь смените ветку: $ git checkout -b part2 # часть2 Затем работайте над второй частью, попутно внося коммиты ваших изменений. Человеку свойственно ошибаться, и часто вы хотите вернуться и поправить что-то в первой части. Если вы везучи или очень искусны, можете пропустить эти строки. $ git checkout master # Возвращаемся к первой части. $ вносим_исправления $ git commit -a # Фиксируем изменения $ git checkout part2 # Возвращаемся ко второй части. $ git merge master # Вливаем сделанные исправления. В конечном счете, первая часть утверждена: $ git checkout master # Возвращаемся к первой части. $ отправка файлов # Выпускаем в мир! $ git merge part2 # Вливаем вторую часть. $ git branch -d part2 # Удаляем ветку part2. Теперь вы снова в ветке master, а вторая часть — в вашем рабочем каталоге. Этот прием легко расширить на любое количество частей. Столь же легко сменить ветку задним числом. Предположим, вы слишком поздно обнаружили, что должны были создать ветку семь коммитов назад. Тогда введите: $ git branch -m master part2 # Переименовываем ветку master в part2. $ git branch master HEAD~7 # Создаем новую ветку master семью коммитами выше. Теперь ветка master содержит только первую часть, а ветка part2 — всё остальное. В последней мы и находимся. Мы создали ветку master, не переключаясь на нее, потому что хотим продолжить работу над part2. Это непривычно: до сих пор мы переключались на ветки сразу же после их создания, вот так: $ git checkout HEAD~7 -b master # Создаем ветку и переключаемся на нее. === Изменяем состав смеси === Предположим, вам нравится работать над всеми аспектами проекта в одной и той же ветке. Вы хотите закрыть свой рабочий процесс от других, чтобы все видели ваши коммиты только после того, как они будут хорошо оформлены. Создайте пару веток: $ git branch sanitized # Создаем ветку для очищенных коммитов. $ git checkout -b medley # Создаем ветку для работы и переключаемся на нее. Далее делайте всё что нужно: исправляйте ошибки, добавляйте новые функции, добавляйте временный код и так далее, при этом почаще выполняя коммиты. После этого $ git checkout sanitized $ git cherry-pick medley^^ применит коммит «пра-родителя» головы ветки «medley» к ветке «sanitized». Правильно подбирая элементы, вы сможете создать ветку, в которой будет лишь окончательный код, а связанные между собой коммиты будут собраны вместе. === Управление Ветками === Для просмотра списка всех веток наберите $ git branch По умолчанию вы начинаете с ветки под названием «master». Кому-то нравится оставлять ветку «master» нетронутой и создавать новые ветки со своими изменениями. Опции *-d* и *-m* позволяют удалять и перемещать (переименовывать) ветки. Смотрите *git help branch*. Ветка «master» — это удобная традиция. Другие могут предполагать, что в вашем хранилище есть ветка с таким именем и что она содержит официальную версию проекта. Хотя вы можете переименовать или уничтожить ветку «master», лучше соблюсти общее соглашение. === Временные Ветки === Через какое-то время вы можете обнаружить, что создаете множество временных веток для одной и той же краткосрочной цели: каждая такая ветка всего лишь сохраняет текущее состояние, чтобы вы могли вернуться назад и исправить серьезную ошибку или сделать что-то еще. Это похоже на то, как вы переключаете телевизионные каналы, чтобы посмотреть что показывают по другим. Но вместо того, чтобы нажать на пару кнопок, вам нужно создавать, выбирать (checkout), сливать (merge) а затем удалять временные ветки. К счастью, в Git есть сокращенная команда, столь же удобная, как пульт дистанционного управления. $ git stash Эта команда сохранит текущее состояние в во временном месте («тайнике», stash) и востановит предыдущее состояние. Ваш каталог становиться точно таким, каким был до начала редактирования, и вы можете исправить ошибки, загрузить удаленные изменения и тому подобное. Когда вы хотите вернуться назад в состояние «тайника», наберите: $ git stash apply # Возможно, понадобится устранить возникшие конфликты. Можно создавать несколько тайников, используя их по-разному. Смотрите *git help stash*. Как вы могли догадаться, Git оставляет ветки «за кадром» при выполнении этого чудесного приема. === Работайте как вам нравится === Возможно, вы сомневаетесь, стоят ли ветки таких хлопот. В конце концов, клоны почти столь же быстрые и вы можете переключаться между ними с помощью *cd* вместо загадочных команд Git. Посмотрим на веб-браузеры. Зачем нужна поддержка вкладок вдобавок к окнам? Поддержка и тех, и других позволяет приспособиться к широкому разнообразию стилей работы. Некоторым пользователям нравится держать открытым единственное окно и использовать вкладки для множества веб-страниц. Другие могут впасть в другую крайность: множество окон без вкладок вообще. Третьи предпочтут нечто среднее. Ветки похожи на вкладки для рабочего каталога, а клоны — на новые окна браузера. Эти операции быстры и выполняются локально, так почему бы не поэкспериментировать и не найти наиболее удобную для себя комбинацию? Git позволяет работать в точности так, как вам нравится. gitmagic-20160304/ru/secrets.txt0000644000175000017500000005103412666307504015642 0ustar sbadiasbadia== Раскрываем тайны == Мы заглянем под капот и объясним, как Git творит свои чудеса. Я опущу излишние детали. За более детальными описаниями обратитесь к http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[руководству пользователя]. === Невидимость === Как Git может быть таким ненавязчивым? За исключением периодических коммитов и слияний, вы можете работать так, как будто и не подозреваете о каком-то управлении версиями. Так происходит до того момента, когда Git вам понадобится, и тогда вы с радостью увидите, что он наблюдал за вами все это время. Другие системы управления версиями вынуждают вас постоянно бороться с загородками и бюрократией. Файлы могут быть доступны только для чтения, пока вы явно не укажете центральному серверу, какие файлы вы намереваетесь редактировать. С увеличением количества пользователей большинство базовых команд начинают выполняться всё медленнее. Неполадки с сетью или с центральным сервером полностью останавливают работу. В противоположность этому, Git просто хранит историю проекта в подкаталоге .git вашего рабочего каталога. Это ваша личная копия истории, поэтому вы можете оставаться вне сети, пока не захотите взаимодействовать с остальными. У вас есть полный контроль над судьбой ваших файлов, поскольку Git в любое время может легко восстановить сохраненное состояние из .git. === Целостность === Большинство людей ассоциируют криптографию с содержанием информации в секрете, но другой столь же важной задачей является содержание ее в сохранности. Правильное использование криптографических хеш-функций может предотвратить случайное или злонамеренное повреждение данных. SHA1 хеш можно рассматривать как уникальный 160-битный идентификатор для каждой строки байт, с которой вы сталкиваетесь в вашей жизни. Даже больше того: для каждой строки байтов, которую любой человек когда-либо будет использовать в течение многих жизней. Так как SHA1 хеш сам является последовательностью байтов, мы можем получить хеш строки байтов, содержащей другие хеши. Это простое наблюдение на удивление полезно: ищите «hash chains» (цепочки хешей). Позднее мы увидим, как Git использует их для эффективного обеспечения целостности данных. Говоря кратко, Git хранит ваши данные в подкаталоге ".git/objects", где вместо нормальных имен файлов вы найдете только идентификаторы. Благодаря использованию идентификаторов в качестве имен файлов, а также некоторым хитростям с файлами блокировок и временны́ми метками, Git преобразует любую скромную файловую систему в эффективную и надежную базу данных. === Интеллект === Как Git узнаёт, что вы переименовали файл, даже если вы никогда не упоминали об этом явно? Конечно, вы можете запустить *git mv*; но это то же самое, что *git rm*, а затем *git add*. Git эвристически находит файлы, которые были переименованы или скопированы между соседними версиями. На деле он может обнаружить, что участки кода были перемещены или скопированы между файлами! Хотя Git не может охватить все случаи, он всё же делает достойную работу, и эта функция постоянно улучшается. Если она не сработала, попробуйте опции, включающие более ресурсоемкое обнаружение копирования и подумайте об обновлении. === Индексация === Для каждого отслеживаемого файла, Git записывает такую информацию, как размер, время создания и время последнего изменения, в файле, известном как «индекс». Чтобы определить, был ли файл изменен, Git сравнивает его текущие характеристики с сохраненными в индексе. Если они совпадают, то Git не станет перечитывать файл заново. Поскольку считывание этой информации значительно быстрее, чем чтение всего файла, то если вы редактировали лишь несколько файлов, Git может обновить свой индекс почти мгновенно. Мы отмечали ранее, что индекс это буферная зона. Почему набор свойств файлов выступает таким буфером? Потому что команда add помещает файлы в базу данных Git и в соответствии с этим обновляет эти свойства; тогда как команда commit без опций создает коммит, основанный только на этих свойствах и файлах, которые уже в базе данных. === Происхождение Git === Это http://lkml.org/lkml/2005/4/6/121[сообщение в почтовой рассылке ядра Linux] описывает последовательность событий, которые привели к появлению Git. Весь этот тред — привлекательный археологический раскоп для историков Git. === База данных объектов === Каждая версия ваших данных хранится в «базе данных объектов», живущей в подкаталоге .git/objects. Другие «жители» .git/ содержат вторичные данные: индекс, имена веток, теги, параметры настройки, журналы, нынешнее расположение «головного» коммита и так далее. База объектов проста и элегантна, и в ней источник силы Git. Каждый файл внутри .git/objects это «объект». Нас интересуют три типа объектов: объекты «блобов», объекты деревьев и объекты коммитов. === Блобы === Для начала один фокус. Выберите имя файла — любое имя файла. В пустом каталоге: $ echo sweet > ВАШЕ_ИМЯ_ФАЙЛА $ git init $ git add . $ find .git/objects -type f Вы увидите +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. Откуда я знаю это, не зная имени файла? Это потому, что SHA1 хеш строки «blob» SP «6» NUL «sweet» LF равен aa823728ea7d592acc69b36875a482cdf3fd5c8d, где SP это пробел, NUL — нулевой байт и LF — перевод строки. Вы можете проверить это, набрав $ printf "blob 6\000sweet\n" | sha1sum Git использует «адресацию по содержимому»: файлы хранятся в соответствии не с именами, а с хешами содержимого, — в файле, который мы называем «блоб-объектом». Хеш можно понимать как уникальный идентификатор содержимого файла, что означает обращение к файлам по их содержимому. Начальный «blob 6» — лишь заголовок, состоящий из типа объекта и его длины в байтах и упрощающий внутренний учет. Таким образом, я могу легко предсказать, что вы увидите. Имя файла не имеет значения: для создания блоб-объекта используется только его содержимое. Вам может быть интересно, что происходит с одинаковыми файлами. Попробуйте добавить копии своего файла с какими угодно именами. Содержание +.git/objects+ останется тем же независимо от того, сколько копий вы добавите. Git хранит данные лишь единожды. Кстати, файлы в каталоге +.git/objects+ сжимаются с помощью zlib поэтому вы не сможете просмотреть их напрямую. Пропустите их через фильтр http://www.zlib.net/zpipe.c[zpipe -d], или введите $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d что выведет указанный объект в читаемом виде. === Деревья === Но где же имена файлов? Они должны храниться на каком-то уровне. Git обращается за именами во время коммита: $ git commit # Введите какое-нибудь описание $ find .git/objects -type f Теперь вы должны увидеть три объекта. На этот раз я не могу сказать вам, что из себя представляют два новых файла, так как это частично зависит от выбранного вами имени файла. Далее будем предполагать, что вы назвали его «rose». Если это не так, то вы можете переписать историю, чтобы она выглядела как будто вы это сделали: $ git filter-branch --tree-filter 'mv ВАШЕ_ИМЯ_ФАЙЛА rose' $ find .git/objects -type f Теперь вы должны увидеть файл +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, так как это SHA1 хеш его содержимого: «tree» SP «32» NUL «100644 rose» NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Проверьте, что этот файл действительно содержит указанную строку, набрав $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch С zpipe легко проверить хеш: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum Проверка хеша с помощью cat-file сложнее, поскольку ее вывод содержит не только «сырой» распакованный файл объекта. Этот файл — объект «дерево» (tree, прим. пер.): список цепочек, состоящих из типа, имени файла и его хеша. В нашем примере: тип файла — 100644, что означает, что «rose» это обычный файл; а хеш — блоб-объект, в котором находится содержимое «rose». Другие возможные типы файлов: исполняемые файлы, символические ссылки или каталоги. В последнем случае, хеш указывает на объект «дерево». Если вы запускали filter-branch, у вас есть старые объекты которые вам больше не нужны. Хотя по окончании срока хранения они будут выброшены автоматически, мы удалим их сейчас, чтобы было легче следить за нашим игрушечным примером: $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune Для реальных проектов обычно лучше избегать таких команд, поскольку вы уничтожаете резервные копии. Если вы хотите иметь чистое хранилище, то обычно лучше сделать свежий клон. Кроме того, будьте осторожны при непосредственном вмешательстве в каталог +.git+: что если другая команда Git работает в это же время, или внезапно произойдет отключение питания? Вообще говоря, ссылки нужно удалять с помощью *git update-ref -d*, хотя обычно ручное удаление +refs/original+ безопасно. === Коммиты === Мы рассмотрели два из трех объектов. Третий объект — «коммит» (commit). Его содержимое зависит от описания коммита, как и от даты и времени его создания. Для соответстия тому, что мы имеем, мы должны немного «подкрутить» Git: $ git commit --amend -m Shakespeare # Изменим описание коммита. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Подделаем временные метки и авторов. $ find .git/objects -type f Теперь вы должны увидеть +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ который является SHA1 хешем его содержимого: «commit 158» NUL «tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9» LF «author Alice 1234567890 -0800» LF «committer Bob 1234567890 -0800» LF LF «Shakespeare» LF Как и раньше, вы сами можете запустить zpipe или cat-file, чтобы увидить это. Это первый коммит, поэтому здесь нет родительских коммитов, но последующие коммиты всегда будет содержать хотя бы одну строку, идентифицирующую родительский коммит. === Неотличимо от волшебства === Секреты Git выглядят слишком простыми. Похоже, что вы могли бы объединить несколько shell-скриптов и добавить немного кода на C, чтобы сделать всё это в считанные часы: смесь базовых операций с файлами и SHA1-хеширования, приправленная блокировочными файлами и fsync для надеждности. По сути, это точное описание ранних версий Git. Тем не менее, помимо гениальных трюков с упаковкой для экономии места и с индексацией для экономии времени, мы теперь знаем, как ловко Git преображает файловую систему в базу данных, идеально подходящую для управления версиями. Например, если какой-либо файл в базе данных объектов поврежден из-за ошибки диска, то его хеш теперь не совпадет, что привлечет наше внимание к проблеме. С помощью хеширования хешей других объектов, мы поддерживаем целостность на всех уровнях. Коммиты атомарны, так что в них никогда нельзя записать лишь часть изменений: мы можем вычислить хеш коммита и сохранить его в базу данных только сохранив все соответствующие деревья, блобы и родительские коммиты. База данных объектов нечувствительна к непредвиденным прерываниям работы, таких как перебои с питанием. Мы наносим поражение даже самым хитрым противникам. Предположим, кто-то пытается тайно изменить содержимое файла в древней версии проекта. Чтобы база объектов выглядела неповрежденной, он также должен изменить хеш соответствующего блоб-объекта, поскольку это теперь другая последовательность байтов. Это означает, что нужно поменять хеши всех объектов деревьев, ссылающихся на этот файл; что в свою очередь изменит хеши всех объектов коммитов с участием таких деревьев; а также и хеши всех потомков этих коммитов. Вследствие этого хеш официальной головной ревизии будет отличаться от аналогичного хеша в этом испорченном хранилище. По цепочке несовпадающих хешей мы можем точно вычислить искаженный файл, как и коммит, где он изначально был поврежден. Одним словом, невозможно подделать хранилище Git, оставив невредимыми двадцать байт, отвечающие последнему коммиту. Как насчет известных характерных особенностей Git? Ветвление? Слияние? Теги? Очевидные подробности. Текущая «голова» хранится в файле +.git/HEAD+, содержащем хеш объекта коммита. Хеш обновляется во время коммита, а также при выполнении многих других команд. С ветками всё аналогично: это файлы в +.git/refs/heads+. То же и тегами: они живут в +.git/refs/tags+, но их обновляет другой набор команд. gitmagic-20160304/ru/multiplayer.txt0000644000175000017500000003646312666307504016552 0ustar sbadiasbadia== Многопользовательский Git == Сначала я использовал Git для личного проекта, в котором был единственным разработчиком. Среди команд, относящихся к распределенным свойствам Git, мне были нужны только *pull* и *clone*, чтобы хранить один и тот же проект в разных местах. Позднее я захотел опубликовать свой код при помощи Git и включать изменения помощников. Мне пришлось научиться управлять проектами, в которых участвуют многие люди по всему миру. К счастью, в этом сильная сторона Git и, возможно, сам смысл его существования. === Кто я? === Каждый коммит содержит имя автора и адрес электронной почты, которые выводятся командой *git log*. По умолчанию Git использует системные настройки для заполнения этих полей. Чтобы установить их явно, введите $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com Чтобы установить эти параметры только для текущего хранилища, опустите флаг --global. === Git через SSH, HTTP === Предположим, у вас есть SSH доступ к веб-серверу, но Git не установлен. Git может связываться через HTTP, хотя это и менее эффективно, чем его собственный протокол. Скачайте, скомпилируйте, установите Git в вашем аккаунте; создайте хранилище в каталоге, доступном через web: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update Для старых версий Git команда копирования не сработает, и вы должны будете запустить $ chmod a+x hooks/post-update Теперь вы можете публиковать свои последние правки через SSH с любого клона: $ git push веб.сервер:/путь/к/proj.git master и кто угодно сможет взять ваш проект с помощью $ git clone http://веб.сервер/proj.git === Git через что угодно === Хотите синхронизировать хранилища без серверов или вообще без сетевого подключения? Вынуждены импровизировать на ходу в непредвиденной ситуации? Мы видели, как <>. Посредством обмена такими файлами мы можем переносить хранилища git любыми доступными средствами, но есть более эффективный инструмент: *git bundle*. Отправитель создает пакет (bundle): $ git bundle create некий-файл HEAD Затем передает «пакет», +некий-файл+, другой команде любыми средствами, как то: электронная почта, флешка, *xxd* печать и последующее распознавание текста, надиктовка битов по телефону, дымовые сигналы и так далее. Получатель восстанавливает коммиты из пакета, введя $ git pull некий-файл Получатель может сделать это даже в пустом хранилище. Несмотря на свой небольшой размер, +некий-файл+ содержит всё исходное хранилище Git. В больших проектах для устранения излишков объема пакетируют только изменения, которых нет в других хранилищах. К примеру, пусть коммит «1b6d…» — последний общий для обеих групп: $ git bundle create некий-файл HEAD ^1b6d Если это делается часто, можно легко забыть, какой коммит был отправлен последним. Справка предлагает для решения этой проблемы использовать теги. А именно, после передачи пакета введите $ git tag -f последний-пакет HEAD и создавайте обновленные пакеты с помощью $ git bundle create новый-пакет HEAD ^последний-пакет === Патчи: общее применение === Патчи это тексты изменений, вполне понятные как человеку, так и компьютеру. Это делает их очень привлекательным форматом обмена. Патч можно послать разработчикам по электронной почте, независимо от того, какую систему управления версиями они используют. Вашим корреспондентам достаточно возможности читать электронную почту, чтобы увидеть ваши изменения. Точно так же, с Вашей стороны требуется лишь адрес электронной почты: нет нужды в настройке онлайн хранилища Git. Вспомним из первой главы: $ git diff 1b6d выводит патч, который может быть вставлен в письмо для обсуждения. В Git хранилище введите $ git apply < мой.patch для применения патча. В более формальных случаях , когда нужно сохранить имя автора и подписи, создавайте соответствующие патчи с заданной точки, набрав $ git format-patch 1b6d Полученные файлы могут быть отправлены с помощью *git-send-email* или вручную. Вы также можете указать диапазон коммитов: $ git format-patch 1b6d..HEAD^^ На принимающей стороне сохраните письмо в файл и введите: $ git am < email.txt Это применит входящие исправления и создаст коммит, включающий имя автора и другую информацию. С web-интерфейсом к электронной почте вам, возможно, потребуется нажать кнопку, чтобы посмотреть электронную почту в своем первоначальном виде перед сохранением патча в файл. Для клиентов электронной почты, использующих mbox, есть небольшие отличия; но если вы используете один из них, то вы, по всей видимости, можете легко разобраться в этом без чтения описаний! === Приносим извинения, мы переехали === После клонирования хранилища команды *git push* или *git pull* автоматически отправляют и получают его по первоначальному адресу. Каким образом Git это делает? Секрет кроется в настройках, заданных при создании клона. Давайте взглянем: $ git config --list Опция +remote.origin.url+ задает исходный адрес; origin — имя первоначального хранилища. Как и имя ветки master, это соглашение. Мы можем изменить или удалить это сокращённое имя, но как правило, нет причин для этого. Если оригинальное хранилище переехало, можно обновить его адрес командой $ git config remote.origin.url git://новый.url/proj.git Опция +branch.master.merge+ задает удаленную ветку по умолчанию для *git pull*. В ходе первоначального клонирования она устанавливается на текущую ветку исходного хранилища, так что даже если HEAD исходного хранилища впоследствии переместится на другую ветку, pull будет верно следовать изначальной ветке. Этот параметр обращается только к хранилищу, которое мы изначально клонировали и которое записано в параметре +branch.master.remote+. При выполнении pull из других хранилищ мы должны указать нужную ветку: $ git pull git://пример.com/other.git master Это объясняет, почему некоторых из наших предыдущих примеров push и pull не имели аргументов. === Удаленные ветки === При клонировании хранилища вы также клонируете все его ветки. Вы можете не заметить этого, потому что Git скрывает их: вы должны запросить их явно. Это предотвращает противоречие между ветками в удаленном хранилище и вашими ветками, а также делает Git проще для начинающих. Список удаленных веток можно посмотреть командой $ git branch -r Вы должны увидеть что-то вроде origin/HEAD origin/master origin/experimental Эти имена отвечают веткам и «голове» в удаленном хранилище; их можно использовать в обычных командах Git. Например, вы сделали много коммитов, и хотели бы сравнить текущее состояние с последней загруженной версией. Вы можете искать в журналах нужный SHA1 хеш, но гораздо легче набрать $ git diff origin/HEAD Также можно увидеть, для чего была создана ветка experimental: $ git log origin/experimental === Несколько удаленных хранилищ === Предположим, что над нашим проектом работают еще два разработчика, и мы хотим следить за обоими. Мы можем наблюдать более чем за одним хранилищем одновременно, вот так: $ git remote add other git://пример.com/некое_хранилище.git $ git pull other некая_ветка Сейчас мы сделали слияние с веткой из второго хранилища. Теперь у нас есть легкий доступ ко всем веткам во всех хранилищах: $ git diff origin/experimental^ other/некая_ветка~5 Но что если мы просто хотим сравнить их изменения, не затрагивая свою работу? Иными словами, мы хотим изучить чужие ветки, не давая их изменениям вторгаться в наш рабочий каталог. Тогда вместо pull наберите $ git fetch # Перенести из origin, по умолчанию. $ git fetch other # Перенести от второго программиста. Так мы лишь переносим их историю. Хотя рабочий каталог остается нетронутыми, мы можем обратиться к любой ветке в любом хранилище команды, работающей с Git, так как теперь у нас есть локальная копия. Держим в уме, что pull это просто *fetch*, а затем *merge*. Обычно мы используем *pull*, потому что мы хотим влить к себе последний коммит после получения чужой ветки. Описанная ситуация — примечательное исключение. О том, как отключить удаленные хранилища, игнорировать отдельные ветки и многом другом смотрите в *git help remote*. === Мои Настройки === Я предпочитаю, чтобы люди, присоединяющиеся к моим проектам, создавали хранилища, из которых я смогу получать изменения с помощью pull. Некоторые хостинги Git позволяют создавать собственные форки проекта в одно касание. После получения дерева из удаленного хранилища я запускаю команды Git для навигации и изучения изменений, в идеале хорошо организованных и описанных. Я делаю слияние со своими изменения и возможно вношу дальнейшие правки. Когда я доволен результатом, я заливаю изменения в главное хранилище. Хотя со мной мало сотрудничают, я верю, что этот подход хорошо масштабируется. Смотрите http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[эту запись в блоге Линуса Торвальдса]. Оставаться в мире Git несколько удобнее, чем использовать файлы патчей, так как это избавляет меня от преобразования их в коммиты Git. Кроме того, Git управляет деталями вроде сохранения имени автора и адреса электронной почты, а также даты и времени, и просит авторов описывать свои изменения. gitmagic-20160304/ru/preface.txt0000644000175000017500000001651412666307504015603 0ustar sbadiasbadia= Волшебство Git = Ben Lynn Август 2007 == От редактора перевода == Не буду долго вас задерживать перед интересным чтением, лишь дам небольшие пояснения по переводу терминологии. Приводя текст к единому стилю, я старался в первую очередь сохранить его цельность и легкость восприятия, а уже затем следовать чистоте языка. Поэтому на русский переведены лишь устоявшиеся термины; в тех случаях, когда общепринятого русского слова нет, была оставлена калька с английского. Например, используется слово «каталог» вместо «директория»; «хранилище» вместо «репозиторий» или «репозитарий»; «слияние» вместо «мерж»; и «ветка» вместо «бранч». Обратные примеры: «коммит», а не «фиксация»; «хук», а не «крюк»; «патч», а не «заплатка». Единственное исключение сделано для фразы «буферная зона» вместо «область стейджинг» и, соответственно, слова «буфер» вместо «стейдж»: поскольку здесь уже не только перевод, но и калька не есть общеупотребительные термины, то лучше было попытаться объяснить смысл понятия. Надеюсь, эти краткие пояснения не оставят для вас неровностей в переводе и позволят погрузиться в текст книги без помех. Приятного чтения. == Предисловие == http://git.or.cz/[Git] это швейцарский нож управления версиями — надежный универсальный многоцелевой инструмент, чья черезвычайная гибкость делает его сложным в изучении даже для многих профессионалов. Как говорил Артур Кларк, любая достаточно развитая технология неотличима от волшебства. Это отличный подход к Git: новички могут игнорировать принципы его внутренней работы и рассматривать Git как нечто восхищающее друзей и приводящее в бешенство врагов своими чудесными способностями. Вместо того, чтобы вдаваться в подробности, мы предоставим приблизительные инструкции для получения конкретных результатов. При частом использовании вы постепенно поймете, как работает каждый трюк и как приспосабливать рецепты под ваши нужды. .Переводы - http://docs.google.com/View?id=dfwthj68_675gz3bw8kj[ Китайский (упрощенный)]: JunJie, Meng и JiangWei. - link:/~blynn/gitmagic/intl/es/[Испанский]: Rodrigo Toledo. - link:/~blynn/gitmagic/intl/de/[Немецкий]: Benjamin Bellee и Armin Stebich. Armin также разместил http://gitmagic.lordofbikes.de/[немецкий перевод на его сайте]. - link:/~blynn/gitmagic/intl/ru/[Русский]: Тихон Тарнавский, Михаил Дымсков и другие. - link:/~blynn/gitmagic/intl/uk/[Украинский]: Владимир Боденчук. - link:/~blynn/gitmagic/intl/fr/[Французский]: Alexandre Garel. Также размещён на http://tutoriels.itaapy.com/[itaapy]. - http://www.slideshare.net/slide_user/magia-git[Португальский]: Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[в формате ODT]]. .Другие варианты - link:book.html[HTML одной страницей]: чистый HTML без CSS. - link:book.pdf[PDF файл]: для печати. - http://packages.debian.org/gitmagic[Пакет Debian], http://packages.ubuntu.com/gitmagic[пакет Ubuntu]: получите локальную копию этого сайта. Придется кстати, http://csdcf.stanford.edu/status/[если этот сервер будет недоступен]. === Благодарности === Я очень ценю, что столь многие люди работали над переводами этих строк. Я благодарен названным выше людям за их усилия, расширившие мою аудиторию. Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin и Tyler Breisacher содействовали в правках и доработках. François Marier сопровождает пакет Debian, изначально созданный Daniel Baumann. Мои благодарности остальным за вашу поддержку и похвалы. Мне очень хотелось процитировать вас здесь, но это могло бы возвысить ваше тщеславие до невообразимых высот. Если я случайно забыл упомянуть вас, пожалуйста, напомните мне или просто вышлите патч. .Бесплатные хостинги Git - http://repo.or.cz/[http://repo.or.cz/] хостинг свободных проектов. Первый сайт Git-хостинга. Основан и поддерживается одним из первых разработчиков Git. - http://gitorious.org/[http://gitorious.org/] другой сайт Git-хостинга, нацеленный на проекты с открытым кодом. - http://github.com/[http://github.com/] хостинг для проектов с открытым кодом; а также для закрытых проектов (на платной основе). Большое спасибо каждому из этих сайтов за размещение этого руководства. === Лицензия === Это руководство выпущено под http://www.gnu.org/licenses/gpl-3.0.html[GNU General Public License 3-й версии]. Естественно, исходный текст находится в хранилище Git и может быть получен командой: $ git clone git://repo.or.cz/gitmagic.git # Создаст каталог "gitmagic". или с одного из зеркал: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git gitmagic-20160304/ru/basic.txt0000644000175000017500000003141712666307504015256 0ustar sbadiasbadia== Базовые операции == Прежде чем погружаться в дебри многочисленных команд Git, попробуйте воспользоваться приведенными ниже простыми примерами, чтобы немного освоиться. Каждый из них полезен, несмотря на свою простоту. На самом деле первые месяцы использования Git я не выходил за рамки материала этой главы. === Сохранение состояния === Собираетесь попробовать внести некие радикальные изменения? Предварительно создайте снимок всех файлов в текущем каталоге с помощью команд $ git init $ git add . $ git commit -m "Моя первая резервная копия" Теперь, если новые правки всё испортили, можно восстановить первоначальную версию: $ git reset --hard Чтобы вновь сохранить состояние: $ git commit -a -m "Другая резервная копия" === Добавление, удаление, переименование === Приведенный выше пример отслеживает только те файлы, которые существовали при первом запуске *git add*. Если вы создали новые файлы или подкаталоги, придется сказать Git'у: $ git add readme.txt Documentation Аналогично, если хотите, чтобы Git забыл о некоторых файлах: $ git rm ляп.h старье.c $ git rm -r улики/ Git удалит эти файлы, если вы не удалили их сами. Переименование файла — это то же, что удаление старого имени и добавления нового. Для этого есть *git mv*, которая имеет тот же синтаксис, что и команда *mv*. Например: $ git mv bug.c feature.c === Расширенные отмена/возврат === Иногда просто хочется вернуться назад и забыть все изменения до определенного момента, потому что все они были неправильными. В таком случае $ git log покажет список последних коммитов и их хеши SHA1: ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob Date: Tue Mar 14 01:59:26 2000 -0800 Заменил printf() на write(). commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alice Date: Thu Jan 1 00:00:00 1970 +0000 Начальный коммит. ---------------------------------- Для указания коммита достаточно первых нескольких символов его хеша, но можете скопировать и весь хеш. Наберите: $ git reset --hard 766f для восстановления состояния до указанного коммита и удаления всех последующих безвозвратно. Возможно, в другой раз вы захотите быстро перескочить к старому состоянию. В этом случае наберите $ git checkout 82f5 Эта команда перенесет вас назад во времени, сохранив при этом более новые коммиты. Однако, как и в фантастических фильмах о путешествиях во времени, если теперь вы отредактируете и закоммитите код, то попадете в альтернативную реальность, потому что ваши действия отличаются от тех, что были в прошлый раз. Эта альтернативная реальность называется «веткой» (branch, прим. пер.), и <>. А сейчас просто запомните, что команда $ git checkout master вернет вас обратно в настоящее. Кроме того, чтобы не получать предупреждений от Git, всегда делайте commit или сбрасывайте изменения перед запуском checkout. Еще раз воспользуемся аналогией с компьютерными играми: - *git reset --hard*: загружает ранее сохраненную игру и удаляет все версии, сохраненные после только что загруженной. - *git checkout*: загружает старую игру, но если вы продолжаете играть, состояние игры будет отличаться от более новых сохранений, которые вы сделали в первый раз. Любая игра, которую вы теперь сохраняете, попадает в отдельную ветку, представляющую альтенативную реальность, в которую вы попали. <>. Можно также восстановить только определенные файлы и подкаталоги, перечислив их имена после команды: $ git checkout 82f5 какой-то.файл другой.файл Будьте внимательны: такая форма *checkout* может молча перезаписать файлы. Чтобы избежать неприятных неожиданностей, выполняйте commit перед checkout, особенно если вы только изучаете Git. Вообще, если вы не уверены в какой-либо операции, будь то команда Git или нет, выполните предварительно *git commit -a*. Не любите копировать и вставлять хеши? Используйте $ git checkout :/"Моя первая р" для перехода на коммит, чье описание начинается с приведенной строки. Можно также запросить 5-ое с конца сохраненное состояние: $ git checkout master~5 === Откаты === В зале суда пункты протокола могут вычеркиваться прямо во время слушания. Подобным образом и вы можете выбирать коммиты для отмены. $ git commit -a $ git revert 1b6d отменит коммит с заданным хешем. Откат будет сохранен в виде нового коммита. Можете запустить *git log*, чтобы убедиться в этом. === Создание списка изменений === Некоторым проектам нужен http://en.wikipedia.org/wiki/Changelog[список изменений] (changelog, прим. пер.). Создайте его такой командой: $ git log > ChangeLog === Скачивание файлов === Получить копию проекта под управлением Git можно, набрав $ git clone git://сервер/путь/до/файлов Например, чтобы получить все файлы, которые я использовал для создания этого документа, $ git clone git://git.or.cz/gitmagic.git Позже мы поговорим о команде *clone* подробнее. === Держа руку на пульсе === Если вы уже загрузили копию проекта с помощью *git clone*, можете обновить ее до последней версии, используя $ git pull === Безотлагательная публикация === Допустим, вы написали скрипт, которым хотите поделиться с другими. Можно просто предложить им скачивать его с вашего компьютера, но если они будут делать это когда вы дорабатываете его или добавляете экспериментальную функциональность, у них могут возникнуть проблемы. Очевидно, поэтому и существуют циклы разработки. Разработчики могут постоянно работать над проектом, но общедоступным они делают свой код только после того, как приведут его в приличный вид. Чтобы сделать это с помощью Git, выполните в каталоге, где лежит ваш скрипт, $ git init $ git add . $ git commit -m "Первый релиз" Затем скажите вашим пользователям запустить $ git clone ваш.компьютер:/путь/до/скрипта чтобы загрузить ваш скрипт. Здесь подразумевается, что у них есть доступ по ssh. Если нет, запустите *git daemon* и скажите пользователям запустить эту команду вместо вышеприведенной: $ git clone git://ваш.компьютер/путь/до/скрипта С этих пор всякий раз, когда ваш скрипт готов к релизу, выполняйте $ git commit -a -m "Следующий релиз" и ваши пользователи смогут обновить свои версии, перейдя в каталог, с вашим скриптом и набрав $ git pull Ваши пользователи никогда не наткнутся на версию скрипта, которую вы не хотите им показывать. === Что я сделал? === Выясните, какие изменения вы сделали со времени последнего коммита: $ git diff Или со вчерашнего дня: $ git diff "@{yesterday}" Или между определенной версией и версией, сделанной 2 коммита назад: $ git diff 1b6d "master~2" В каждом случае на выходе будет патч, который может быть применен с помощью *git apply*. Попробуйте также: $ git whatchanged --since="2 weeks ago" Часто вместо этого я использую для просмотра истории http://sourceforge.net/projects/qgit[qgit], из-за приятного интерфейса, или http://jonas.nitro.dk/tig[tig] с текстовым интерфейсом, который хорошо работает через медленное соединение. Как вариант, установите веб-сервер, введите *git instaweb* и запустите любой веб-браузер. === Упражнение === Пусть A, B, C, D — четыре последовательных коммита, где В отличается от A лишь несколькими удаленными файлами. Мы хотим вернуть эти файлы в D. Как мы можем это сделать? Существует как минимум три решения. Предположим, что мы находимся на D. 1. Разница между A и B — удаленные файлы. Мы можем создать патч, отражающий эти изменения, и применить его: $ git diff B A | git apply 2. Поскольку в коммите A мы сохранили файлы, то можем восстановить их: $ git checkout A foo.c bar.h 3. Мы можем рассматривать переход от A к B как изменения, которые хотим отменить: $ git revert B Какой способ лучше? Тот, который вам больше нравится. С помощью Git легко получить желаемое, и часто существует много способов это сделать. gitmagic-20160304/ru/grandmaster.txt0000644000175000017500000004274612666307504016513 0ustar sbadiasbadia== Гроссмейстерство Git == Теперь вы уже должны уметь ориентироваться в страницах *git help* и понимать почти всё. Однако точный выбор команды, необходимой для решения конкретной проблемы, может быть утомительным. Возможно, я сберегу вам немного времени: ниже приведены рецепты, пригодившиеся мне в прошлом. === Релизы исходников === В моих проектах Git управляет в точности теми файлами, которые я собираюсь архивировать и пускать в релиз. Чтобы создать тарбол с исходниками, я выполняю: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === Коммит изменений === В некоторых проектах может быть трудоемко оповещать Git о каждом добавлении, удалении и переименовании файла. Вместо этого вы можете выполнить команды $ git add . $ git add -u Git просмотрит файлы в текущем каталоге и сам позаботится о деталях. Вместо второй команды add, выполните *git commit -a*, если вы собираетесь сразу сделать коммит. Смотрите *git help ignore*, чтобы узнать как указать файлы, которые должны игнорироваться. Вы можете выполнить все это одним махом: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove Опции *-z* и *-0* предотвращают неверную обработку файловых имен, содержащих специальные символы. Поскольку эта команда добавляет игнорируемые файлы, вы возможно захотите использовать опции -x или -X. === Мой коммит слишком велик === Вы пренебрегали коммитами слишком долго? Яростно писали код и вспомнили об управлении исходниками только сейчас? Внесли ряд несвязанных изменений, потому что это ваш стиль? Нет поводов для беспокойства. Выполните $ git add -p Для каждой сделанной вами правки Git покажет измененный участок кода и спросит, должно ли это изменение попасть в следующий коммит. Отвечайте «y» (да) или «n» (нет). У вас есть и другие варианты, например отложить выбор; введите «?» чтобы узнать больше. Когда закончите, выполните $ git commit для внесения именно тех правок, что вы выбрали («буферизованных» изменений). Убедитесь, что вы не указали опцию *-a*, иначе Git закоммитит все правки. Что делать, если вы изменили множество файлов во многих местах? Проверка каждого отдельного изменения становится удручающей рутиной. В этом случае используйте *git add -i*. Ее интерфейс не так прост, но более гибок. В несколько нажатий кнопок можно добавить или убрать из буфера несколько файлов одновременно, либо просмотреть и выбрать изменения лишь в отдельных файлах. Как вариант, запустите *git commit \--interactive*, которая автоматически сделает коммит когда вы закончите. === Индекс — буферная зона Git === До сих пор мы избегали знаменитого «индекса» Git, но теперь мы должны рассмотреть его, для пояснения вышесказанного. Индекс это временный буфер. Git редко перемещает данные непосредственно между вашим проектом и его историей. Вместо этого Git сначала записывает данные в индекс, а уж затем копирует их из индекса по месту назначения. Например, *commit -a* на самом деле двухэтапный процесс. Сначала слепок текущего состояния каждого из отслеживаемых файлов помещается в индекс. Затем слепок, находящийся в индексе, записывается в историю. Коммит без опции *-a* выполняет только второй шаг, и имеет смысл только после выполнения команд, изменяющих индекс, таких как *git add*. Обычно мы можем не обращать внимания на индекс и делать вид, что взаимодействуем напрямую с историей. Но в данном случае мы хотим более тонкого контроля, поэтому управляем индексом. Мы помещаем слепок некоторых (но не всех) наших изменений в индекс, после чего окончательно записываем этот аккуратно сформированный слепок. === Не теряй «головы» === Тег HEAD (англ. «голова», прим. пер.) — как курсор, который обычно указывает на последний коммит, продвигаясь с каждым новым коммитом. Некоторые команды Git позволяют перемещать этот курсор. Например, $ git reset HEAD~3 переместит HEAD на три коммита назад. Теперь все команды Git будут работать так, как будто вы не делали последних трех коммитов, хотя файлы останутся в текущем состоянии. В справке описано несколько способов использования этого приема. Но как вернуться назад в будущее? Ведь предыдущие коммиты о нем ничего не знают. Если у вас есть SHA1 изначальной «головы», то: $ git reset 1b6d Но допустим, вы его не записывали. Не беспокойтесь: для комнад такого рода Git сохраняет оригинальную «голову» как тег под названием ORIG_HEAD, и вы можете вернуться надежно и безопасно: $ git reset ORIG_HEAD === Охота за «головами» === Предположим ORIG_HEAD недостаточно. К примеру, вы только что осознали, что допустили громадную ошибку, и вам нужно вернуться к древнему коммиту в давно забытой ветке. По умолчанию Git хранит коммиты не меньше двух недель, даже если вы приказали уничтожить содержащую их ветку. Проблема в нахождении соответствующего хеша. Вы можете просмотреть все значения хешей в .git/objects и методом проб и ошибок найти нужный. Но есть путь значительно легче. Git записывает каждый подсчитанный им хеш коммита в .git/logs. В подкатлоге refs содержится полная история активности на всех ветках, а файл HEAD содержит каждое значение хеша, которое когда-либо принимал HEAD. Последнее можно использовать чтобы найти хеши коммитов на случайно обрубленных ветках. Команда reflog предоставляет удобный интерфейс работы с этими журналами. Используйте $ git reflog Вместо копирования хешей из reflog, попробуйте $ git checkout "@{10 minutes ago}" # 10 минут назад, прим. пер. Или сделайте чекаут пятого с конца из посещенных коммитов с помощью $ git checkout "@{5}" Смотрите раздел «Specifying Revisions» в *git help rev-parse* для дополнительной информации. Вы можете захотеть удлинить отсрочку для коммитов, обреченных на удаление. Например, $ git config gc.pruneexpire "30 days" означает, что удаляемые коммиты будут окончательно исчезать только по прошествии 30 дней и после запуска *git gc*. Также вы можете захотеть отключить автоматический вызов *git gc*: $ git config gc.auto 0 В этом случае коммиты будут удаляться только когда вы будете запускать *git gc* вручную. === Git как основа === Дизайн Git, в истинном духе UNIX, позволяет легко использовать его как низкоуровневый компонент других программ: графических и веб-интерфейсов; альтернативных интерфейсов командной строки; инструментов управления патчами; средств импорта или конвертации, и так далее. Многие команды Git на самом деле — скрипты, стоящие на плечах гигантов. Небольшой доработкой вы можете переделать Git на свой вкус. Простейший трюк — использование алиасов Git для сокращения часто используемых команд: $ git config --global alias.co checkout $ git config --global --get-regexp alias # отображает текущие алиасы alias.co checkout $ git co foo # то-же, что и «git checkout foo» Другой пример: можно выводить текущую ветку в приглашении командной строки или заголовке окна терминала. Запуск $ git symbolic-ref HEAD выводит название текущей ветки. На практике вы скорее всего захотите убрать «refs/heads/» и сообщения об ошибках: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- Подкаталог +contrib+ это целая сокровищница инструментов, построенных на Git. Со временем некоторые из них могут становиться официальными командами. В Debian и Ubuntu этот каталог находится в +/usr/share/doc/git-core/contrib+. Один популярный инструмент из этого каталога — +workdir/git-new-workdir+. Этот скрипт создает с помощью символических ссылок новый рабочий каталог, имеющий общую историю с оригинальным хранилищем: $ git-new-workdir существующее/хранилище новый/каталог Новый каталог и файлы в нем можно воспринимать как клон, с той разницей, что два дерева автоматически остаются синхронизированными ввиду общей истории. Нет необходимости в merge, push и pull. === Рискованные трюки === Нынешний Git делает случайное уничтожение данных очень сложным. Но если вы знаете, что делаете, вы можете обойти защиту для распространенных команд. *Checkout*: Наличие незакоммиченных изменений прерывает выполнение checkout. Чтобы перейти к нужному коммиту, даже уничтожив свои изменения, используйте «принуждающий» (force, прим. пер.) флаг *-f*: $ git checkout -f HEAD^ С другой стороны, если вы укажете checkout конкретные пути, проверки на безопасность не будет: указанные файлы молча перезапишутся. Будьте осторожны при таком использовании checkout. *Reset*: сброс также прерывается при наличии незакоммиченных изменений. Чтобы заставить его сработать, запустите $ git reset --hard 1b6d *Branch*: Удаление ветки прервётся, если оно привело бы к потере изменений. Для принудительного удаления введите $ git branch -D мертвая_ветка # вместо -d Аналогично, попытка перезаписи ветки путем перемещения будет прервана, если может привести к потере данных. Для принудительного перемещений ветки введите $ git branch -M источник цель # вместо -m В отличии от checkout и reset, эти две команды дают отсрочку в удалении данных. Изменения остаются в каталоге .git и могут быть возвращены восстановлением нужного хеша из .git/logs (смотрите выше раздел «Охота за „головами“»). По умолчанию они будут храниться по крайней мере две недели. *Clean*: Некоторые команды могут не сработать из опасений повредить неотслеживаемые файлы. Если вы уверены, что все неотслеживаемые файлы и каталоги не нужны, то безжалостно удаляйте их командой $ git clean -f -d В следующий раз эта досадная команда сработает! === Предотвращаем плохие коммиты === Глупые ошибки загрязняют мои хранилища. Самое ужасное это проблема недостающих файлов, вызванная забытым *git add*. Примеры менее серьезных проступков: завершающие пробелы и неразрешённые конфликты слияния. Несмотря на безвредность, я не хотел бы, чтобы это появлялось в публичных записях. Если бы я только поставил защиту от дурака, используя _хук_, который бы предупреждал меня об этих проблемах: $ cd .git/hooks $ cp pre-commit.sample pre-commit # В старых версиях Git: chmod +x pre-commit Теперь Git отменит коммит, если обнаружит лишние пробелы или неразрешенные конфликты. Для этого руководства я в конце концов добавил следующее в начало хука *pre-commit*, чтобы защититься от своей рассеянности: if git ls-files -o | grep '\.txt$'; then echo ПРЕРВАНО! Неотслеживаемые .txt файлы. exit 1 fi Хуки поддерживаются несколькими различными операциями Git, смотрите *git help hooks*. Мы использовали пример хука *post-update* раньше, при обсуждении использования Git через http. Он запускался при каждом перемещении «головы». Пример скрипта *post-update* обновляет файлы, которые нужны Git для связи через не считающиеся с ним средства сообщения, такие как HTTP. gitmagic-20160304/ru/history.txt0000644000175000017500000004430712666307504015700 0ustar sbadiasbadia== Уроки истории == Вследствие распределенной природы Git, историю изменений можно легко редактировать. Однако, если вы вмешиваетесь в прошлое, будьте осторожны: изменяйте только ту часть истории, которой владеете вы и только вы. Иначе, как народы вечно выясняют, кто же именно совершил и какие бесчинства, так и у вас будут проблемы с примирением при попытке совместить разные деревья истории. Некоторые разработчики убеждены, что история должна быть неизменна со всеми огрехами и прочим. Другие считают, что деревья нужно делать презентабельными перед выпуском их в публичный доступ. Git учитывает оба мнения. Переписывание истории, как и клонирование, ветвление и слияние, — лишь еще одна возможность, которую дает вам Git. Разумное ее использование зависит только от вас. === Оставаясь корректным === Только что сделали коммит и поняли, что должны были ввести другое описание? Запустите $ git commit --amend чтобы изменить последнее описание. Осознали, что забыли добавить файл? Запустите *git add*, чтобы это сделать, затем выполните вышеуказанную команду. Захотелось добавить еще немного изменений в последний коммит? Так сделайте их и запустите $ git commit --amend -a === …И кое-что еще === Давайте представим, что предыдущая проблема на самом деле в десять раз хуже. После длительной работы вы сделали ряд коммитов; но вы не очень-то довольны тем, как они организованы, и кое-какие описания коммитов надо бы слегка переформулировать. Тогда запустите $ git rebase -i HEAD~10 и последние десять коммитов появятся в вашем любимом редакторе (задается переменной окружения $EDITOR). Например: pick 5c6eb73 Добавил ссылку repo.or.cz pick a311a64 Переставил аналогии в «Работай как хочешь» pick 100834f Добавил цель для push в Makefile Теперь вы можете: - Убирать коммиты, удаляя строки. - Менять порядок коммитов, переставляя строки. - Заменять «pick» на: * «edit» для внесения правок в коммиты; * «reword» для изменения описания в журнале; * «squash» для слияния коммита с предыдущим; * «fixup», чтобы слить коммит с предыдущим, отбросив его описание. Сохраните файл и закройте редактор. Если вы отметили коммит для исправлений, запустите $ git commit --amend Если нет, запустите $ git rebase --continue Одним словом, делайте коммиты как можно раньше и как можно чаще — вы всегда сможете навести порядок при помощи rebase. === Локальные изменения сохраняются === Предположим, вы работаете над активным проектом. За какое-то время вы делаете несколько коммитов, затем синхронизируетесь с официальным деревом через слияние. Цикл повторяется несколько раз, пока вы не будете готовы влить изменения в центральное дерево. Однако теперь история изменений в локальном клоне Git представляет собой кашу из ваших и официальных изменений. Вам бы хотелось видеть все свои изменения непрерывной линией, а затем — все официальные изменения. Это работа для команды *git rebase*, описанной выше. Зачастую, имеет смысл использовать флаг *--onto* и убрать переплетения. Также смотрите *git help rebase* для получения подробных примеров использования этой замечательной команды. Вы можете расщеплять коммиты. Вы можете даже переупорядочивать ветки. === Переписывая историю === Время от времени вам может понадобиться в системе управления версиями аналог «замазывания» людей на официальных фотографиях, как бы стирающего их из истории в духе сталинизма. Например, предположим, что мы уже собираемся выпустить релиз проекта, но он содержит файл, который не должен стать достоянием общественности по каким-то причинам. Возможно, я сохранил номер своей кредитки в текстовый файл и случайно добавил его в проект. Удалить файл недостаточно: он может быть доступен из старых коммитов. Нам надо удалить файл из всех ревизий: $ git filter-branch --tree-filter 'rm совершенно/секретный/файл' HEAD Смотрите *git help filter-branch*, где обсуждается этот пример и предлагается более быстрый способ решения. Вообще, *filter-branch* позволяет изменять существенные части истории при помощи одной-единственной команды. После этой команды каталог +.git/refs/original+ будет описывать состояние, которое было до ее вызова. Убедитесь, что команда filter-branch сделала то, что вы хотели, и если хотите опять использовать эту команду, удалите этот каталог. И, наконец, замените клоны вашего проекта исправленной версией, если собираетесь в дальнейшем с ними взаимодействовать. === Создавая Историю === [[makinghistory]] Хотите перевести проект под управление Git? Если сейчас он находится под управлением какой-либо из хорошо известных систем управления версиями, то вполне вероятно, что кто-нибудь уже написал необходимые скрипты для экспорта всей истории проекта в Git. Если нет, то смотрите в сторону команды *git fast-import*, которая считывает текстовый ввод в специальном формате для создания истории Git с нуля. Обычно скрипт, использующий эту команду, бывает слеплен наспех для единичного запуска, переносящего весь проект за один раз. В качестве примера вставьте такие строки во временный файл, вроде '/tmp/history': ---------------------------------- commit refs/heads/master committer Alice Thu, 01 Jan 1970 00:00:00 +0000 data < int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob Tue, 14 Mar 2000 01:59:26 -0800 data < int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- Затем создайте хранилище Git из этого временного файла при помощи команд: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history Вы можете извлечь последнюю версию проекта с помощью $ git checkout master . Команда *git fast-export* преобразует любое хранилище в формат, понятныый команде *git fast-import*. Ее вывод можно использовать как образец для написания скриптов преобразования, или для переноса хранилищ в понятном человеку формате. Конечно, с помощью этих команд можно пересылать хранилища текстовых файлов через каналы передачи текста. === Когда же все пошло не так? === Вы только что обнаружили, что кое-какой функционал вашей программы не работает, но вы совершенно отчетливо помните, что он работал всего несколько месяцев назад. Ох… Откуда же взялась ошибка? Вы же это проверяли сразу как разработали. В любом случае, уже слишком поздно. Однако, если вы фиксировали свои изменения достаточно часто, то Git сможет точно указать проблему: $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git извлечет состояние ровно посередине. Проверьте работает ли то, что сломалось, и если все еще нет, $ git bisect bad Если же работает, то замените «bad» на «good». Git снова переместит вас в состояние посередине между «хорошей» и «плохой» ревизиями, сужая круг поиска. После нескольких итераций, этот двоичный поиск приведет вас к тому коммиту, на котором возникла проблема. После окончания расследования, вернитесь в исходное состояние командой $ git bisect reset Вместо ручного тестирования каждого изменения автоматизируйте поиск, запустив $ git bisect run my_script По возвращаемому значению заданной команды, обычно одноразового скрипта, Git будет отличать хорошее состояние от плохого. Скрипт должен вернуть 0, если нынешний коммит хороший; 125, если его надо пропустить; и любое другое число от 1 до 127, если он плохой. Отрицательное возвращаемое значение прерывает команду bisect. Вы можете сделать многим больше: страница помощи поясняет, как визуализировать bisect, проанализировать или воспроизвести ее журнал, или исключить заведомо хорошие изменения для ускорения поиска. === Из-за кого все пошло не так? === Как и во многих других системах управления версиями, в Git есть команда blame (ответственность, прим. пер.): $ git blame bug.c Она снабжает каждую строку выбранного файла примечаниями, раскрывающими, кто и когда последним ее редактировал. В отличие же от многих других систем управления версиями, эта операция происходит без соединения с сетью, выбирая данные с локального диска. === Личный опыт === В централизованных системах управления версиями изменения истории — достаточно сложная операция, и доступна она лишь администраторам. Клонирование, ветвление и слияние невозможны без взаимодействия по сети. Так же обстоят дела и с базовыми операциями, такими как просмотр истории или фиксация изменений. В некоторых системах сетевое соединение требуется даже для просмотра собственных изменений, или открытия файла для редактирования. Централизованные системы исключают возможность работы без сети и требуют более дорогой сетевой инфраструктуры, особенно с увеличением количества разработчиков. Что важнее, все операции происходят медленнее, обычно до такой степени, что пользователи избегают пользоваться «продвинутыми» командами без крайней необходимости. В радикальных случаях это касается даже большинства базовых команд. Когда пользователи вынуждены запускать медленные команды, производительность страдает из-за прерываний рабочего процесса. Я испытал этот феномен на себе. Git был моей первой системой управления версиями. Я быстро привык к нему и стал относится к его возможностям как к должному. Я предполагал, что и другие системы похожи на него: выбор системы управления версиями не должен отличаться от выбора текстового редактора или браузера. Когда немного позже я был вынужден использовать централизованную систему управления версиями, я был шокирован. Ненадежное интернет-соединение не имеет большого значения при использовании Git, но делает разработку невыносимой, когда от него требуют надежности как у жесткого диска. Вдобавок я обнаружил, что стал избегать некоторых команд из-за задержек в их выполнении, что помешало мне следовать предпочтительному рабочему процессу. Когда мне было нужно запустить медленную команду, нарушение хода моих мыслей оказывало несоизмеримый ущерб разработке. Ожидая окончания связи с сервером, я вынужден был заниматься чем-то другим, чтобы скоротать время; например, проверкой почты или написанием документации. К тому времени, как я возвращался к первоначальной задаче, выполнение команды было давно закончено, но мне приходилось тратить уйму времени, чтоб вспомнить, что именно я делал. Люди не очень приспособлены для переключения между задачами. Кроме того, есть интересный эффект «трагедии общественных ресурсов»: предвидя будущую перегруженность сети, некоторые люди в попытке предотвратить грядущие задержки начинают использовать более широкие каналы, чем им реально требуется для текущих задач. Суммарная активность увеличивает загрузку сети, поощряя людей задействовать всё более высокоскоростные каналы для предотвращения еще больших задержек. gitmagic-20160304/ru/translate.txt0000644000175000017500000000406712666307504016173 0ustar sbadiasbadia== Приложение B: Перевод этого руководства == Я советую следующий способ для перевода этого руководства, чтобы мои скрипты могли быстро создавать HTML и PDF версии, а все переводы находились в одном хранилище. Клонируйте исходные тексты, затем создайте каталог, отвечающий тегу IETF целевого языка: смотрите http://www.w3.org/International/articles/language-tags/Overview.en.php[статью W3C по интернационализации]. К примеру, английский язык это «en», а японский — «ja». Скопируйте в каталог файлы +txt+ из каталога «en» и переведите их. К примеру, для перевода руководства на http://ru.wikipedia.org/wiki/%D0%9A%D0%BB%D0%B8%D0%BD%D0%B3%D0%BE%D0%BD%D1%81%D0%BA%D0%B8%D0%B9_%D1%8F%D0%B7%D1%8B%D0%BA[клингонский язык], вы можете набрать: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # «tlh» — языковой код IETF клингонского языка. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Переведите файл. и так с каждым файлом. Отредактируйте Makefile и добавьте код языка в переменную TRANSLATIONS. Теперь вы сможете просматривать вашу работу по ходу дела: $ make tlh $ firefox book.html Почаще делайте коммиты, а когда ваш перевод будет готов, сообщите мне об этом. На GitHub есть веб-интерфейс, облегчающий описанные действия: сделайте форк проекта «gitmagic», залейте ваши изменения и попросите меня сделать слияние. gitmagic-20160304/ru/intro.txt0000644000175000017500000003067712666307504015337 0ustar sbadiasbadia== Введение == Чтобы объяснить, что такое управление версиями, я буду использовать аналогии. Если нужно более точное объяснение, обратитесь к http://ru.wikipedia.org/wiki/%D0%A1%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F_%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D1%8F%D0%BC%D0%B8[статье википедии]. === Работа - это игра === Я играл в компьютерные игры почти всю свою жизнь. А вот использовать системы управления версиями начал уже будучи взрослым. Полагаю, я такой не один, и сравнение этих двух занятий может помочь объяснению и пониманию концепции. Представьте, что редактирование кода или документа — игра. Далеко продвинувшись, вы захотите сохраниться. Для этого вы нажмете на кнопку «Сохранить» в вашем любимом редакторе. Но это перезапишет старую версию. Это как в древних играх, где был только один слот для сохранения: конечно, вы можете сохраниться, но вы больше никогда не сможете вернуться к более раннему состоянию. Это досадно, так как прежнее сохранение могло указывать на одно из очень интересных мест в игре, и может быть, однажды вы захотите вернуться к нему. Или, что еще хуже, вы сейчас находитесь в безвыигрышном положении и вынуждены начинать заново. === Управление версиями === Во время редактирования вы можете «Сохранить как…» в другой файл, или скопировать файл куда-нибудь перед сохранением, чтобы уберечь более старые версии. Может быть, заархивировав их для экономии места на диске. Это самый примитивный вид управления версиями, к тому же требующий интенсивной ручной работы. Компьютерные игры прошли этот этап давным-давно, в большинстве из них есть множество слотов для сохранения с автоматическими временны́ми метками. Давайте немного усложним условия. Пусть у вас есть несколько файлов, используемых вместе, например, исходный код проекта или файлы для вебсайта. Теперь, чтобы сохранить старую версию, вы должны скопировать весь каталог. Поддержка множества таких версий вручную неудобна и быстро становится дорогим удовольствием. В некоторых играх сохранение — это и есть каталог с кучей файлов внутри. Игры скрывают детали от игрока и предоставляют удобный интерфейс для управления различными версиям этого каталога. В системах управления версиями всё точно так же. У них у всех есть приятный интерфейс для управления каталогом с вашим скарбом. Можете сохранять состояние каталога так часто, как пожелаете, а затем восстановить любую из предыдущих сохраненных версий. Но, в отличие от компьютерных игр, они существенно экономят дисковое пространство. Обычно от версии к версии изменяется только несколько файлов, и то ненамного. Хранение лишь различий вместо полных копий требует меньше места. === Распределенное управление === А теперь представьте очень сложную компьютерную игру. Ее настолько сложно пройти, что множество опытных игроков по всему миру решили объединиться и использовать общие сохранения, чтобы попытаться выиграть. Прохождения на скорость — живой пример. Игроки, специализирующиеся на разных уровнях игры, объединяются, чтобы в итоге получить потрясающий результат. Как бы вы организовали такую систему, чтобы игроки смогли легко получать сохранения других? А загружать свои? В былые времена каждый проект использовал централизованное управление версиями. Какой-нибудь сервер хранил все сохраненные игры. И никто больше. Каждый держал лишь несколько сохранений на своей машине. Когда игрок хотел пройти немного дальше, он выкачивал самое последнее сохранение с главного сервера, играл немного, сохранялся и закачивал уже свое сохранение обратно на сервер, чтобы остальные могли им воспользоваться. А что если игрок по какой-то причине захотел использовать более старую сохраненную игру? Возможно, нынешнее сохранение безвыигрышно, потому что кто-то забыл взять некий игровой предмет еще на третьем уровне, и нужно найти последнее сохранение, где игру всё еще можно закончить. Или, может быть, хочется сравнить две более старые сохраненные игры, чтобы установить вклад конкретного игрока. Может быть много причин вернуться к более старой версии, но выход один: нужно запросить ту старую сохраненную игру у центрального сервера. Чем больше сохраненных игр требуется, тем больше понадобится связываться с сервером. Системы управления версиями нового поколения, к которым относится Git, известны как распределенные системы, их можно понимать как обобщение централизованных систем. Когда игроки загружаются с главного сервера, они получают каждую сохраненную игру, а не только последнюю. Они как бы зеркалируют центральный сервер. Эти первоначальные операции клонирования могут быть ресурсоемкими, особенно при длинной истории, но сполна окупаются при длительной работе. Наиболее очевидная прямая выгода состоит в том, что если вам зачем-то потребуется более старая версия, взаимодействие с сервером не понадобится. === Глупые предрассудки === Широко распространенное заблуждение состоит в том, что распределенные системы непригодны для проектов, требующих официального централизованного хранилища. Ничто не может быть более далеким от истины. Получение фотоснимка не приводит к тому, что мы крадем чью-то душу. Точно так же клонирование главного хранилища не уменьшает его важность. В первом приближении можно сказать, что все, что делает централизованная система управления версиями, хорошо сконструированная распределенная система может сделать лучше. Сетевые ресурсы просто дороже локальных. Хотя дальше мы увидим, что в распределенном подходе есть свои недостатки, вы вряд ли ошибетесь в выборе, руководствуясь этим приближенным правилом. Небольшому проекту может понадобиться лишь частица функционала, предлагаемого такой системой. Но использование плохо масштабируемой системы для маленьких проектов подобно использованию римских цифр в расчетах с небольшими числами. Кроме того, проект может вырасти сверх первоначальных ожиданий. Использовать Git с самого начала — это как держать наготове швейцарский нож, даже если вы всего лишь открываете им бутылки. Однажды вам безумно понадобится отвертка и вы будете рады, что под рукой есть нечто большее, чем простая открывалка. === Конфликты при слиянии === Для этой темы аналогия с компьютерной игрой становится слишком натянутой. Вместо этого, давайте вернемся к редактированию документа. Итак, допустим, что Алиса вставила строчку в начале файла, а Боб — в конце. Оба они закачивают свои изменения. Большинство систем автоматически сделает разумный вывод: принять и соединить их изменения так, чтобы обе правки — и Алисы, и Боба — были применены. Теперь предположим, что и Алиса, и Боб внесли разные изменения в одну и ту же строку. В этом случае невозможно продолжить без человеческого вмешательства. Тот из них, кто вторым закачает на сервер изменения, будет информирован о _конфликте слияния_ (merge conflict), и должен либо предпочесть одно изменение другому, либо скорректировать всю строку. Могут случаться и более сложные ситуации. Системы управления версиями разрешают простые ситуации сами и оставляют сложные для человека. Обычно такое их поведение поддается настройке. gitmagic-20160304/ru/clone.txt0000644000175000017500000004277712666307504015310 0ustar sbadiasbadia== Все о клонировании == В старых системах управления версиями стандартная операция для получения файлов — это checkout. Вы получаете набор файлов в конкретном сохраненном состоянии. В Git и других распределенных системах управления версиями стандартный способ — клонирование. Для получение файлов вы создаете «клон» всего хранилища. Другими словами, вы фактически создаете зеркало центрального сервера. При этом всё, что можно делать с основным хранилищем, можно делать и с локальным. === Синхронизация компьютеров === Я вполне приемлю создание архивов или использование *rsync* для резервного копирования и простейшей синхронизации. Но я работаю то на ноутбуке, то на стационарном компьютере, которые могут никак между собой не взаимодействовать между этим. Создайте хранилище Git и закоммитьте файлы на одном компьютере. А потом выполните на другом $ git clone первый.компьютер:/путь/к/файлам для создания второго экземпляра файлов и хранилища Git. С этого момента команды $ git commit -a $ git pull другой.компьютер:/путь/к/файлам HEAD будут «втягивать» состояние файлов с другого компьютера на тот, где вы работаете. Если вы недавно внесли конфликтующие изменения в один и тот же файл, Git даст вам знать, и нужно будет сделать коммит заново после разрешения ситуации. === Классическое управление исходным кодом === Создайте хранилище Git для ваших файлов: $ git init $ git add . $ git commit -m "Начальный коммит" На центральном сервере создайте так называемое «голое» (bare) хранилище Git в неком каталоге: $ mkdir proj.git $ cd proj.git $ git init --bare $ # вариант «в одну строчку»: GIT_DIR=proj.git git init Запустите Git-демон, если необходимо: $ git daemon --detach # возможно уже запущен Для создания нового пустого хранилища Git на публичных серверах следуйте их инструкциям. Обычно, нужно заполнить форму на веб-странице. Отправьте ваши изменения в центральное хранилище вот так: $ git push git://центральный.сервер/путь/к/proj.git HEAD Для получения ваших исходников разработчик вводит $ git clone git://центральный.сервер/путь/к/proj.git После внесения изменений разработчик сохраняет изменения локально: $ git commit -a Для обновления до последней версии: $ git pull Любые конфликты слияния нужно разрешить и закоммитить: $ git commit -a Для выгрузки локальных изменений в центральное хранилище: $ git push Если на главном сервере были новые изменения, сделанные другими разработчиками, команда push не сработает. В этом случае разработчику нужно будет вытянуть к себе (pull) последнюю версию, разрешить возможные конфликты слияний и попробовать еще раз. === Голые (bare) хранилища === Голое (bare) хранилище называются так потому, что у него нет рабочего каталога. Оно содержит только файлы, которые обычно скрыты в подкаталоге .git. Другими словами, голое хранилище содержит историю изменений, но не содержит снимка какой-либо определенной версии. Голое хранилище играет роль, похожую на роль основного сервера в централизованной системе управления версиями: это дом вашего проекта. Разработчики клонируют из него проект и закачивают в него свежие официальные изменения. Как правило, оно располагается на сервере, который не делает почти ничего кроме раздачи данных. Разработка идет в клонах, поэтому домашнее хранилище может обойтись и без рабочего каталога. Многие команды Git не работают в голых хранилищах, если переменная среды GIT_DIR не содержит путь до хранилища и не указан параметр --bare. === Push или pull? === Зачем вводится команда push, вместо использования уже знакомой pull? Прежде всего, pull не работает в голых хранилищах, вместо нее нужно использовать команду fetch, которая будет рассмотрена позже. Но даже если держать на центральном сервере нормальное хранилище, использование команды pull в нем будет затруднительным. Нужно будет сначала войти на сервер интерактивно и сообщить команде pull адрес машины, с которой мы хотим забрать изменения. Этому могут мешать сетевые брандмауэры (firewall), но в первую очередь: что если у нас нет интерактивного доступа к серверу? Тем не менее, не рекомендутся push-ить в хранилище помимо этого случая — из-за путаницы, которая может возникнуть, если у целевого хранилища есть рабочий каталог. Короче говоря, пока изучаете Git, push-те только в голые хранилища. В остальных случаях pull-те. === Создание форка проекта === Не нравится путь развития проекта? Думаете, можете сделать лучше? Тогда на вашем сервере выполните $ git clone git://основной.сервер/путь/к/файлам Теперь расскажите всем о форке (ответвлении, прим. пер.) проекта на вашем сервере. Позже вы сможете в любой момент втянуть к себе изменения из первоначального проекта: $ git pull === Максимальные бэкапы === Хотите иметь множество защищенных, географически разнесенных запасных архивов? Если в вашем проекте много разработчиков, ничего делать не нужно! Каждый клон — это и есть резервная копия; не только текущего состояния, но и всей истории изменений проекта. Благодаря криптографическому хешированию, повреждение какого-либо из клонов будет обнаружено при первой же попытке взаимодействия с другими клонами. Если ваш проект не такой популярный, найдите как можно больше серверов для размещения клонов. Особо беспокоящимся рекомендуется всегда записывать самый последний 20-байтный SHA1 хеш HEAD в каком-нибудь безопасном месте. Оно должно быть безопасным, а не тайным. Например, хороший вариант — публикация в газете, потому что атакующему сложно изменить каждый экземпляр газеты. === Многозадачность со скоростью света === Скажем, вы хотите работать над несколькими функциями параллельно. Тогда закоммитьте ваши изменения и запустите $ git clone . /некий/новый/каталог Благодаря http://ru.wikipedia.org/wiki/жёсткая_ссылка[жёстким ссылкам] создание локального клона требует меньше времени и места, чем простое копирование. Теперь вы можете работать с двумя независимыми функциями одновременно. Например, можно редактировать один клон, пока другой компилируется. В любой момент можно сделать коммит и вытянуть изменения из другого клона: $ git pull /другой/клон HEAD === Партизанское управление версиями === Вы работаете над проектом, который использует другую систему управления версиями, и вам очень не хватает Git? Тогда создайте хранилище Git в своем рабочем каталоге: $ git init $ git add . $ git commit -m "Начальный коммит" затем склонируйте его: $ git clone . /некий/новый/каталог Теперь перейдите в этот новый каталог и работайте в нем вместо основного, используя Git в свое удовольствие. В какой-то момент вам понадобиться синхронизировать изменения со всеми остальными — тогда перейдите в изначальный каталог, синхронизируйте его с помощью другой системы управления версиями и наберите $ git add . $ git commit -m "Синхронизация с остальными" Теперь перейдите в новый каталог и запустите $ git commit -a -m "Описание моих изменений" $ git pull Процедура передачи изменений остальным зависит от другой системы управления версиями. Новый каталог содержит файлы с вашими изменениями. Запустите команды другой системы управления версиями, необходимые для загрузки файлов в центральное хранилище. Subversion (вероятно, наилучшая централизованная система управления версиями) используется неисчислимым множеством проектов. Команда *git svn* автоматизирует описанный процесс для хранилищ Subversion, а также может быть использована для http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[экспорта проекта Git в хранилище Subversion]. === Mercurial === Mercurial — похожая система управления версиями, которая может работать в паре с Git практически без накладок. С расширением hg-git пользователь Mercurial может без каких либо потерь push-ить и pull-ить из хранилища Git. Получить hg-git можно с помощью Git: $ git clone git://github.com/schacon/hg-git.git или Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ К сожалению, мне неизвестен аналогичное расширение для Git. Поэтому я рекомендую использовать Git, а не Mercurial, для центрального хранилища, даже если вы предпочитаете Mercurial. Для проектов, использующих Mercurial, обычно какой-нибудь доброволец поддерживает параллельное хранилище Git для привлечения пользователей последнего, тогда как проекты, использующие Git, благодаря hg-git автоматически доступны пользователям Mercurial. Хотя расширение может сконвертировать хранилище Mercurial в Git путем push'а в пустое хранилище, эту задачу легче решить, используя сценарий hg-fast-export.sh, доступный как $ git clone git://repo.or.cz/fast-export.git Для преобразования выполните в пустом каталоге $ git init $ hg-fast-export.sh -r /hg/repo после добавления сценария в ваш $PATH. === Bazaar === Упомянем вкратце Bazaar, так как это самая популярная свободная распределенная система управления версиями после Git и Mercurial. Bazaar относительно молод, поэтому у него есть преимущество идущего следом. Его проектировщики могут учиться на ошибках предшественников и избавиться от исторически сложившихся неровностей. Кроме того, его разработчики заботятся о переносимости и взаимодействии с другими системами управления версиями. Расширение bzr-git позволяет (в какой-то степени) пользователям Bazaar работать с хранилищами Git. Программа tailor конвертирует хранилища Bazaar в Git и может делать это с накоплением, тогда как bzr-fast-export хорошо приспособлена для разовых преобразований. === Почему я использую Git === Изначально я выбрал Git потому, что слышал, что он в состоянии справиться с совершенно неуправляемыми исходными текстами ядра Linux. Я никогда не ощущал потребности сменить его на что-то другое. Git работает замечательно и мне еще только предстоит напороться на его недостатки. Так как я в основном использую Linux, проблемы на других системах меня не касаются. Я также предпочитаю программы на C и сценарии на bash исполняемым файлам вроде сценариев на Python-е: у них меньше зависимостей, и я привык к быстрому выполнению. Я думал о том, как можно улучшить Git, вплоть до того, чтобы написать собственный инструмент, похожий на Git; но только как академическое упражнение. Завершив проект, я бы все равно продолжил пользоваться Git, потому что выигрыш слишком мал, чтобы оправдать использование самодельной системы. Естественно, ваши потребности и пожелания вероятно отличаются от моих и вы, возможно, лучше уживетесь с другой системой. И всё же вы не слишком ошибетесь, используя Git. gitmagic-20160304/makeover0000755000175000017500000000250312666307504014537 0ustar sbadiasbadia#!/bin/bash # Extract table of contents from index.html and delete preface link. BOOKDIR=book-$1 TITLE="Git Magic" case $1 in ru) TITLE="Волшебство Git" ;; esac gawk ' /
/ { print $0 getline print $0 print "
  • '"$TITLE"'
  • " getline while (!match($0, "
    ")) { gsub("pr01.html", "index.html") print $0 getline } print $0 exit } ' < $BOOKDIR/index.html > toc.tmp # For every file except the index... for FILE in $BOOKDIR/*.html do sed '//a <link href="http://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css">' -i $FILE if [ $FILE != "$BOOKDIR/index.html" ] then # Prepend "Git Magic - " to titles of all pages. sed '/<title>/ s/<title>/&'"$TITLE"' - /' -i $FILE sed 's/pr01\.html/index.html/g' -i $FILE # Paste ToC into beginning and add div section with class content for CSS. sed '/<body/{n; r toc.tmp a <div class="content"> }' -i $FILE sed '/^<\/body/i </div>' -i $FILE fi done # Originally this link was "My Homepage". Since it appears on translations of # the guide, I changed it to my name so it doesn't have to be translated. sed '/^<\/body/i </div><div class="footer"><a href="/~blynn/">Ben Lynn</a></div>' -i $BOOKDIR/*.html cp $BOOKDIR/pr01.html $BOOKDIR/index.html rm toc.tmp ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/Makefile��������������������������������������������������������������������������0000644�0001750�0001750�00000006252�12666307504�014445� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# The availaible translation languages. # When starting a new translation, add a language code here. # TRANSLATIONS = de es fr ko pt_br ru uk vi zh_cn zh_tw it pl LANGS = en $(TRANSLATIONS) SHELL := /bin/bash .PHONY: all clean sync public distclean $(LANGS) all: $(LANGS) $(LANGS): %: book-% book-%/default.css book-%.html book-%.pdf # The book consists of these text files in the following order: TXTFILES := preface.txt intro.txt basic.txt clone.txt branch.txt history.txt \ multiplayer.txt grandmaster.txt secrets.txt drawbacks.txt translate.txt $(foreach l,$(LANGS),book-$(l).xml): book-%.xml: $(addprefix %/,$(TXTFILES)) # Concatenate the text files and feed to AsciiDoc. # If a file has not yet been translated for the target language, # then substitute the English version. # Kludge to support any translation of "Preface". echo '[specialsections]' > conf ; \ if [ $* != ru ]; then \ sed -n '/^== .* ==$$/p' $*/preface.txt | sed 's/^== \(.*\) ==$$/^\1$$=preface/' >> conf ; \ else \ cp lang-ru.conf conf ; fi ; \ ( for FILE in $^ ; do if [ -f $$FILE ]; then cat $$FILE; else \ cat en/$$(basename $$FILE); fi; echo ; done ) | \ asciidoc -a lang=$* -d book -b docbook -f conf - > $@ # This rule allows unfinished translations to build. # Report an error if the English version of the text file is missing. $(addprefix en/,$(TXTFILES)): @if [ ! -f $@ ]; then echo English file missing: $@; exit 123; fi $(foreach l,$(TRANSLATIONS),$(addprefix $(l)/,$(TXTFILES))): @if [ ! -f $@ ]; then echo $@ missing: using English version; fi # Ignore tidy's exit code because Asciidoc generates section IDs beginning with # "_", which xmlto converts to "id" attributes of <a> tags. The standard # insists that "id" attributes begin with a letter, which causes tidy to # print a warning and return a nonzero code. # # When Asciidoc 8.3.0+ is widespread, I'll use its idprefix attribute instead # of ignoring return codes. $(foreach l,$(LANGS),book-$(l)): book-%: book-%.xml xmlto -m custom-html.xsl -o book-$* html book-$*.xml sed -i'' -e 's/xmlns:fo[^ ]*//g' book-$*/*.html -ls book-$*/*.html | xargs -n 1 tidy -utf8 -m -i -q ./makeover $* $(foreach l,$(LANGS),book-$(l)/default.css): book-%/default.css: book.css -mkdir -p book-$* rsync book.css book-$*/default.css $(foreach l,$(LANGS),book-$(l).html): book-%.html: book-%.xml xmlto -m custom-nochunks.xsl html-nochunks $^ -tidy -utf8 -imq $@ # Set SP_ENCODING to avoid "non SGML character" errors. # Can also do SP_ENCODING="UTF-8". $(foreach l,$(LANGS),book-$(l).pdf): book-%.pdf: book-%.xml if [ $* = zh_cn -o $* = zh_tw ]; then \ if ! [ -f fop-$*.xsl ]; then wget -q http://bestrecords.net/fop/fop-$*.xsl; fi; \ if ! [ -f fop-$*.xconf ]; then wget -q http://bestrecords.net/fop/fop-$*.xconf; fi; \ xmlto -m fop-$*.xsl --with-fop -p "-c `pwd`/fop-$*.xconf" pdf book-$*.xml ;\ elif [ $* = vi ] ; then \ xsltproc --encoding utf-8 fop-vi.xsl book-$*.xml > book-$*.fo; \ fop -c fop-vi.xconf -fo book-$*.fo -pdf book-$*.pdf; \ else \ SP_ENCODING="XML" docbook2pdf book-$*.xml; \ fi clean: -rm -rf $(foreach l,$(LANGS),book-$(l).pdf book-$(l).xml book-$(l).html book-$(l)) \ *.fo *.log *.out *.aux conf distclean: clean -rm -rfv fop fop-zh* ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/�������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12666307504�013416� 5����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/README�������������������������������������������������������������������������0000644�0001750�0001750�00000000466�12666307504�014304� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������This project translate GitMagic ebook into Vietnamese. License: GPL-3 Copyleft: 2010-2011 GitMagic team. First transalted by: Trần Ngọc Quân. Personal website: vnwildman.users.sourceforge.net Project's SCM address: https://github.com/vnwildman/gitmagic.git Special thanks: -Clytie Siddall. -GitHub.com ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/drawbacks.txt������������������������������������������������������������������0000644�0001750�0001750�00000024124�12666307504�016123� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Phụ lục A: Hạn chế của Git == Git bộc lộ một số nhược điểm mà tôi đã gặp qua. Một số có thể xử lý thủ công một cách dễ dàng bằng các đoạn kịch bản và hook, một số yêu cầu phải tổ chức lại hay xác lập lại dự án, một số ít rắc rối còn lại, chỉ còn cách là ngồi đợi. Hay tốt hơn cả là bắt tay vào và giúp đỡ họ viết! === Điểm Yếu SHA1 === Thời gian trôi đi, những nhà mật mã đã phát hiện ra ngày càng nhiều điểm yếu của thuật toán SHA1. Thực tế người ta đã đã phát hiện thấy sự va chạm giá trị băm. Trong khoảng vài năm, có lẽ những chiếc PC thông thường cũng đủ sức để âm thầm làm hư hỏng một kho Git. Hy vọng là Git sẽ chuyển sang sử dụng hàm băm tốt hơn trước khi có người tìm ra cách phá mã SHA1. === Microsoft Windows === Sử dụng Git trên hệ điều hành Microsoft Windows có vẻ hơi cồng kềnh một chút: - http://cygwin.com/[Cygwin], mô phỏng Linux dành cho Windows, có chứa http://cygwin.com/packages/git/[Git đã chuyển đổi để chạy trên Windows]. - http://code.google.com/p/msysgit/[Git chạy trên MSys] là một thay thế với các hỗ trợ tối thiểu nhất, bởi vì chỉ cần một ít lệnh để thực hiện một số việc mà thôi. === Các Tập tin Không liên quan === Nếu dự án của bạn rất lớn và chứa rất nhiều tập tin không có liên quan mà luôn luôn bị thay đổi, Git có thể chịu thiệt thòi hơn các hệ thống khác bởi vì các tập tin không được giữ dấu viết từng cái riêng lẻ. Git giữ các dấu vết thay đổi cho toàn bộ dự án, điều này thường là có lợi. Giải pháp là chia nhỏ dự án của bạn ra, mỗi một phần bao gồm các tập tin liên quan đến nhau. Hãy sử dụng *git submodule* nếu bạn vẫn muốn giữ mọi thứ trong một kho chung. === Ai Sửa và Sửa gì? === Một số hệ thống quản lý mã nguồn bắt buộc bạn đánh dấu rõ ràng vào tập tin theo một cách nào đó trước khi biên soạn. Trong khi mà điều này đặc biệt phiền toái vì nó lại dính líu đến việc phải liên lạc với máy chủ trung tâm, việc làm này có hai lợi ích: 1. Thực hiện lệnh 'diff' diễn ra nhanh bởi vì nó chỉ kiểm tra các tập tin đã đánh dấu. 2. Một người có thể biết được khác đang làm việc trên một tập tin bằng cách hỏi máy chủ trung tâm ai đã đánh dấu là đang sửa. Với một đoạn kịch bản thích hợp, bạn có thể lưu giữ theo cách này với. Điều này yêu cầu sự hợp tác từ người lập trình, người có thể chạy các kịch bản chuyên biệt khi biên soạn một tập tin. === Lịch Sử Tập Tin === Sau khi Git ghi lại các thay đổi cho các dự án lớn, việc cấu trúc lại lịch sử của một tập tin đơn lẻ yêu cầu phải làm việc nhiều hơn các chương trình quản lý mã nguồn giữ dấu vết theo các tập tin riêng lẻ. Thiệt hại thường là không đáng kể, và thứ đáng giá mà nó nhận được là các tác vụ khác hoạt động hiệu quả đến không ngờ. Ví dụ, `git checkout` nhanh hơn `cp -a`, và dữ liệu trong dự án lớn nén tốt hơn việc gom lại từ tập tin cơ bản. === Khởi tạo Bản Sao === Việc tạo một bản sao có vẻ hơi xa xỉ hơn là việc 'checkout' trong các hệ thống quản lý mã nguồn khác khi phần mềm có lịch sử phát triển lâu dài. Cái giá phải trả ban đầu là cần nhiều thời gian để lấy về, nhưng nếu đã làm như thế, các tác vụ cần làm sau này sẽ nhanh chóng và không cần có mạng. Tuy nhiên, trong một số hoàn cảnh, cách làm phù hợp hơn là tạo một bản sao không đầy đủ bằng tùy chọn `--depth`. Điều này giúp ta tạo bản sao nhanh hơn, nhưng bản sao nhận được sẽ thiếu đi một số chức năng do đó bạn sẽ không thể thực thi được một số lệnh. === Các Dự Án Hay Thay Đổi === Git được viết ra với mục đích chú tâm đến kích thước tạo ra bởi các thay đổi. Con người chỉ tạo ra sự thay đổi rất nhỏ giữa các phiên bản. Như là bổ xung lời nhận xét là có sửa lỗi ở đây, có đặc tính mới ở đây, sửa lỗi chú thích, v.v.. Nhưng nếu các tập tin của bạn căn bản khác nhau, thì trong mỗi lần commit, nó sẽ ghi lại toàn bộ các thay đổi vào lịch sử và làm cho dự án của bạn tất yếu sẽ tăng kích cỡ. Không có bất kỳ một hệ thống quản lý mã nguồn nào có thể làm được điều này, nhưng những người sử dụng Git theo tiêu chuẩn sẽ còn phải chịu tổn thất hơn khi lịch sử của nó được nhân bản. Đây là lý do tại sao các thay đổi quá lớn cần được xem xét. Định dạng các tập tin có thể bị thay đổi. Các thay đổi nhỏ chỉ xảy ra phần lớn tại một số ít tập tin. Việc xét đến việc sử dụng cơ sở dữ liệu hay các giải pháp sao-lưu/lưu-trữ có lẽ là thứ có vẻ thực tế hơn, không nên dùng hệ thống quản lý mã nguồn. Ví dụ, quản lý mã nguồn không thích hợp cho việc quản lý các ảnh được chụp một cách định kỳ từ webcam. Nếu các tập tin thực sự thay đổi thường xuyên và chúng cần phải quản lý, việc xem xét khả năng sử dụng Git hoạt động như một hệ thống quản lý tập trung là có thể chấp nhận được. Một người có thể tải về một bản sao không đầy đủ, nó chỉ lấy về một ít hay không lấy về lịch sử của dự án. Dĩ nhiên, nhiều công cụ dành cho Git sẽ không thể hoạt động được, và sự sửa chữa phải được chuyển lên như là các miếng vá. Điều này chắc chắn là tốt và nó giống như là ta không thể hiểu nổi tại sao một số người lại muốn có được lịch sử của rất nhiều các tập tin chẳng hoạt động ổn định. Một ví dụ khác là dự án phụ thuộc vào firmware, cái này có dạng thức là tập tin nhị phân có kích thước rất lớn. Người sử dụng không quan tâm tới lịch sử của firmware, vả lại khả năng nén của nó lại cũng rất ít, vì vậy quản lý firmware có lẽ là không cần thiết vì nó làm phình to kích thước kho chứa. Trong trường hợp này, mã nguồn có thể lưu giữ trong kho Git, và tập tin nhị phân được giữ ở nơi khác. Để cho công việc trở nên dễ dàng hơn, một người có thể tạo ra một đoạn kịch bản mà nó sử dụng Git để nhân bản mã nguồn, và dùng lệnh rsync hay Git lấy về firmware. === Bộ Đếm === Một số hệ quản trị mã nguồn tập trung duy trì một số nguyên dương tự động tăng lên khi có lần commit mới được chấp nhận. Git quy các thay đổi này bởi giá trị băm của chúng, điều này là tốt trong phần lớn hoàn cảnh. Nhưng một số người thích có nó ở dạng số nguyên. May mắn thay, rất dễ dàng để viết các đoạn kịch bản làm được như thế với mỗi lần cập nhật, kho Git trung tâm Git gia một số nguyên, có thể là trong một thẻ (tag), và kết hợp nó với giá trị băm của lần commit cuối. Mỗi bản sao có thể có một bộ đếm riêng, nhưng điều này chẳng ích lợi gì, chỉ có kho chứa trung tâm và bộ đếm của nó là có ý nghĩa với mọi người. === Với Thư Mục Rỗng === Các thư mục rỗng không được theo dõi. Tạo ra các thư mục giả để thử trục trặc này. Xét về mặt thi hành của Git, thay vì thiết kế của nó, điều hạn chế này này là đáng trách. Với một chút may mắn, một khi Git thấy có thêm lợi ích từ việc này, thêm nhiều người đòi hỏi tính năng này và nó sẽ được thực hiện. === Lần Commit Khởi tạo === Hệ thống số đếm khoa học của máy tính đếm từ 0, thay vì 1. Thật không may, có liên quan đến các lần commit, Git không tôn trọng quy ước này. Rất nhiều lệnh bất lợi trước lần commit khởi tạo. Thêm nữa, các trường hợp ngoại lệ phải được xử lý theo một cách đặc biệt, như là việc rebasing một nhánh với lần commit khởi tạo khác. Git có thể có được lợi ích từ việc định nghĩa lần commit zero: ngay khi kho chứa được tạo ra, HEAD được đặt cho một chuỗi ký tự bao gồm 20 byte rỗng. Lần commit đặc biệt này tương ứng với một cây (tree) rỗng, không có gốc, tại một thời điểm được đề lùi về trước. Sau đó chạy lệnh git log, ví dụ thế, thì Git nên báo cho người dùng biết chưa có lần commit nào, thay vì phát ra một lỗi nghiêm trọng. Điều tương tự xảy ra với các công cụ khác. Tất cả các bản commit đầu tiên hoàn toàn là con cháu của bản 0 (zero). Tuy nhiên, ở đây có một số vấn đề xảy ra trong một số trường hợp đặc biệt. Nếu nhiều nhánh với các lần khởi tạo commit khác nhau được trộn với nhau, sau đó rebase kết quả đòi hỏi thực chất có sự can thiệp bằng tay. === Giao diện chưa rõ ràng === Để commit A và B, nghĩa của biểu thức "A..B" và "A...B" tùy thuộc vào việc lệnh mong đó là hai đầu mút hay là một vùng. Xem *git help diff* và *git help rev-parse*. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/branch.txt���������������������������������������������������������������������0000644�0001750�0001750�00000040652�12666307504�015423� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Thủ Thuật Tạo Nhánh == Tạo Nhánh và Trộn là các đặc tính sát thủ của Git. *Vấn đề đặt ra*: Những nhân tố bên ngoài chắc hẳn đòi hỏi cần hoán chuyển văn cảnh. Một lỗi tồi tệ xuất hiện trong phiên bản đã được phát hành mà không được cảnh báo gì. Điều tồi tệ nhất có thể xảy ra là phải xóa bỏ hẳn đặc tính kỹ thuật đó. Người phát triển phần mềm, người mà đã giúp bạn viết nó, cần biết lý do về việc bãi bỏ. Trong tất cả các trường hợp trên, bạn buộc phải xóa bỏ cái mà bạn đang làm và làm một cái hoàn toàn mới. Việc gán đoạn suy nghĩ có thể làm giảm hiệu suất làm việc, và việc hoán chuyển nội dung càng cồng kềnh, vướng víu càng gây hậu quả nặng nề. Đối với các hệ thống quản lý mã nguồn tập trung chúng ta phải tải về một bản sao các tập tin mới từ máy chủ trung tâm. Các hệ thống phân tán hoạt động hiệu quả hơn, như là chúng ta có thể nhân bản một cách cục bộ. Nhưng việc nhân bản bắt buộc phải sao chép toàn bộ thư mục làm việc cũng như là toàn bộ các mục trong lịch sử cho đến thời điểm đã được chỉ ra. Dù là Git giảm bớt sự lãng phí cho việc này bằng cách chia sẻ và tạo ra các liên kết tập tin cứng, chính bản thân các tập tin dự án cũng phải được tạo ra trong các đề mục của chúng trong thư mục làm việc. *Giải pháp*: Git có một công cụ tốt hơn để sử lý tình huống này, nó nhanh và tiết kiệm không gian lưu trữ hơn lệnh nhân bản đó chính là: *git branch*. Với vài câu thần chú, các tập tin trong thư mục của bạn dễ dàng biến đổi từ phiên bản này sang phiên bản khác. Sự chuyển đổi này có thể làm nhiều hơn việc di chuyển trong trong lịch sử một các đơn thuần. Các tập tin của bạn có thể chuyển hình thái từ bản phát hành cuối thành phiên bản thử nghiệm, thành phiên bản phát triển hiện nay, thành phiên bản của người bạn của bạn, và cứ như thế. === Nút Điều Khiển === Mỗi khi chơi điện tử, bạn bấm vào nút (``nút điều khiển''), màn hình có lẽ hiển thị ngay ra một cái bảng hay một thứ gì đó? Thế thì nhỡ ông chủ của bạn đang đi lại trong văn phòng nơi bạn đang chơi điện tử thì làm cách nào để nhanh chóng giấu chúng đi? Ở thư mục nào đó: $ echo "Tôi thông minh hơn xếp của mình" > myfile.txt $ git init $ git add . $ git commit -m "Lần commit đầu tiên" Chúng ta đã tạo ra kho chứa Git mà nó theo dõi một tập tin văn bản có chứa một thông điệp đã biết trước. Giờ hãy gõ: $ git checkout -b boss # dường như chẳng có gì thay đổi sau lệnh này $ echo "Xếp thông minh hơn tôi" > myfile.txt $ git commit -a -m "Lần chuyển giao khác" Điều này cũng giống như việc chúng ta ghi đè lên tập tin của mình sau đó commit nó. Nhưng không, đó chỉ là ảo tưởng. Gõ: $ git checkout master # quay trở lại phiên bản nguyên gốc của tập tin Ối trời ơi! Tập tin văn bản lại trở về như cũ mất rồi. Và nếu ông chủ có ý định ngó qua thư mục của bạn thì hãy gõ: $ git checkout boss # chuyển trở lại phiên bản vừa mắt ông chủ Bạn có thể hoán chuyển giữa hai phiên bản của tập tin tùy thích, và commit từng cái trong số chúng một cách độc lập. === Bản Nháp === [[branch]] Bạn nói rằng mình đang làm việc với một số đặc tính kỹ thuật, và vì lý do nào đó, bạn muốn quay trở lại bản cách đây ba bản và tạm thời đặt thêm vài dòng lệnh in ra màn hình để có thể thấy được một số hàm hoạt động như thế nào. Thế thì: $ git commit -a $ git checkout HEAD~3 Giờ thì bạn có thể thêm những dòng mã lệnh tạm thời ở đâu mình muốn. Bạn còn có thể commit những thay đổi đó. Khi bạn đã làm xong, hãy thực hiện lệnh $ git checkout master để quay lại công việc chính. Chú ý là bất kỳ các thay đổi không được commit sẽ đổ xuống sông xuống biển. Nhưng bạn lại muốn ghi lại các thay đổi tạm thời đó sau khi đã làm xong? Rất dễ: $ git checkout -b dirty và commit trước khi quay trở lại nhánh master. Khi nào đó bạn muốn quay trở lại các thay đổi ở trên, đơn giản, chỉ cần gõ: $ git checkout dirty Chúng ta đã đụng chạm đến lệnh như trên ở những chương trước rồi, khi thảo luận về việc tải về một trạng thái cũ. Cuối cùng chúng ta có thể thuật lại toàn bộ câu chuyện: các tập tin đã thay đổi theo trạng thái đã được yêu cầu, nhưng chúng ta phải rời bỏ nhánh master. Tất cả những lần commit được tạo ra từ đây sẽ dẫn bạn đi trên một nhánh khác, nhánh này có thể được đặt tên sau. Mặt khác, sau khi 'check out' một trạng thái cũ, Git tự động đặt bạn vào một trạng thái mới, một nhánh chưa có tên, và nhánh này có thể đặt tên và ghi lại với lệnh *git checkout -b*. === Sửa Nhanh === Bạn đang phân vân giữa ngã ba đường khi bạn phải quyết định là xóa tất cả mọi thứ hoặc là sửa chữa các lỗi mới phát hiện ra trong lần commit `1b6d...`: $ git commit -a $ git checkout -b fixes 1b6d # checkout và đặt tên là nhánh fixes Sau khi hoàn tất việc sửa chữa: $ git commit -a -m "Đã sửa" $ git checkout master và sau đó quay lại công việc theo phận sự của mình. Bạn thậm chí có thể trộn với lần commit đã sửa để sửa lỗi: $ git merge fixes === Trộn === Với một số hệ thống quản lý mã nguồn, việc tạo các nhánh rất dễ dàng nhưng trộn chúng trở lại là một bài toán hóc búa. Với Git, việc trộn là dễ dàng và bạn có thể không hay biết nó hoạt động như thế nào. Chúng ta đã sử dụng việc trộn từ lâu rồi. Lệnh *pull* trên thực tế đã 'fetch' (lấy về) các lần commit và sau đó trộn chúng vào trong nhánh hiện hành của bạn. Nếu trên máy của mình bạn không có thay đổi gì cả, thế thì việc trộn sẽ là một 'fast forward' (chuyển tiếp nhanh), trường hợp này cũng na ná như việc lấy về phiên bản cuối cùng trong hệ thống quản lý mã nguồn tập trung. Nhưng nếu bạn đã có thay đổi trên máy của mình, Git sẽ tự động trộn, và báo lỗi cho bạn nếu có xung đột xảy ra. Thông thường, mỗi lần commit có một 'commit cha', tạm gọi thế, chính là lần commit trước. Việc trộn các nhánh với nhau phát sinh ra một lần commit với ít nhất hai 'cha'. Điều này đặt ra câu hỏi: lần commit mà `HEAD~10` thực sự ám chỉ đến là lần nào? Một lần commit có thể có nhiều cha, thế thì chúng ta phải theo cái nào? Nó sẽ gọi ra 'cha' đầu tiên. Đây là điều ta mong muốn bởi vì nhánh hiện hành trở thành cha đầu tiên trong suốt quá trình trộn; thường, bạn chỉ liên quan đến những thay đổi mình tạo ra trong nhánh hiện hành, cốt để mà đối lập với việc trộn thay đổi từ các nhánh khác. Bạn hãy nhớ Git quy một cha nào đó với một dấu mũ. Ví dụ, để hiển thị nhật ký tính từ 'cha' thứ hai: $ git log HEAD^2 Bạn có thể bỏ qua số dành cho cha đầu tiên. Ví dụ, để hiển thị sự khác nhau với cha đầu tiên: $ git diff HEAD^ Bạn có thể tổ hợp các dấu mũ này với các kiểu khác nhau. Ví dụ: $ git checkout 1b6d^^2~10 -b ancient bắt đầu một nhánh mới ``ancient'' tương ứng với trạng thái lần commit thứ 10 trở về trước từ cha thứ hai của cha thứ nhất của lần commit bắt đầu với 1b6d. === Làm Việc Liên Tục === Thường trong các dự án phần cứng, bước thứ hai của kế hoạch phải chờ bước thứ nhất hoàn thành. Một chiếc xe hơi cần sửa chữa có thể phải nằm chờ trong xưởng sửa chữa cho đến khi các chi tiết phụ tùng đặc biệt được chuyển đến từ nhà máy. Một mẫu có thể phải chờ một con chip được làm ra trước khi quá trình chế tác có thể tiếp tục. Dự án phần mềm cũng có thể tương tự như thế. Bộ phận thứ hai có một số tính năng có thể phải chờ cho đến khi phần thứ nhất đã được phát hành và kiểm tra. Một số dự án yêu cầu mã nguồn của bạn phải được xem xét lại trước khi chấp nhận nó, vì vậy bạn có thể phải chờ cho đến khi bộ phận thứ nhất đã được chấp thuận trước khi bắt đầu phần thứ hai. Nhờ có việc tạo nhánh và trộn dễ dàng và cũng chẳng mất mát gì, chúng ta có thể phá vỡ quy tắc và làm việc trên Part II trước khi Part I chính thức sẵn sàng. Giả sử bạn đã commit Part I và gửi nó đi để xem xét. Giả sử bạn đang ở nhánh `master`. Thế thì hãy phân nhánh ra: $ git checkout -b part2 Tiếp đến, làm việc trên Part II, commit những thay đổi của bạn bao nhiêu tùy thích. Lỗi là ở con người, và bạn sẽ phải thường xuyên quay trở lại để sửa lỗi nào đó trong Part I. Nếu may mắn, hay trong trường hợp tốt, bạn có thể bỏ qua những dòng này. $ git checkout master # Quay trở lại Part I. $ sửa_lỗi $ git commit -a # Commit sau khi sửa lỗi. $ git checkout part2 # Quay trở lại Part II. $ git merge master # Trộn các thay đổi. Cuối cùng, Part I được chấp thuận: $ git checkout master # Quay trở lại Part I. $ submit files # Xuất bản ra! $ git merge part2 # Trộn vào Part II. $ git branch -d part2 # Xóa nhánh "part2". Bây giờ chúng ta lại ở trong nhánh `master`, với Part II trong thư mục làm việc. Thủ thuật này rất dễ dàng để mở rộng ra dành cho nhiều phần hơn. Nó cũng đễ dàng để phân nhánh ra từ quá khứ: giả sử muộn bạn mới nhận ra là mình phải tạo một nhánh từ trước đây 7 lần commit. Thế thì gõ: $ git branch -m master part2 # Đổi tên nhánh "master" thành "part2". $ git checkout HEAD~7 -b master # Tạo nhánh "master" mới 7 lần commit ngược từ trước. Nhánh `master` bây giờ chỉ chứa Part I, và nhánh `part2` trở thành nhánh chứa. Chúng ta đang ở nhánh sau; chúng ta đã tạo nhánh `master` mà không chuyển đến nó, bởi vì chúng ta muốn tiếp tục làm việc trên `part2`. Điều này hơi bất thường. Cho đến lúc này, Chúng ta chuyển đến các nhánh ngay sau khi chúng được tạo ra, như là trong: $ git checkout HEAD~7 -b master # Tạo một nhánh và chuyển tới nó. === Cải Tổ Lại Sự Pha Trộn === Có lẽ bạn thích làm việc trên mọi khía cạnh của một dự án trên cùng một nhánh. Bạn muốn giữ riêng các thay đổi mình đang làm cho riêng mình và muốn những người khác chỉ thấy được các lần commit của bạn sau khi đã được tổ chức lại. Hãy chuẩn bị một cặp nhánh: $ git branch -b sanitized # Tạo một nhánh dùng cho việc dọn. $ git checkout -b medley # Tạo và chuyển nhánh thành nơi làm việc. Tiếp theo, làm việc gì đó: sửa lỗi, thêm các đặc tính kỹ thuật, thêm mã lệnh tạm thời, vân vân, commit thường xuyên. Sau đó: $ git checkout sanitized # tạm dịch: đã được vệ sinh $ git cherry-pick medley^^ # tạm dịch: hỗn độn; ^^: ông bà áp dụng nhánh ông-bà của lần commit head của nhánh ``medley'' thành nhánh ``sanitized''. Với lệnh thích hợp là cherry-picks bạn có thể cấu trúc một nhánh mà nó chỉ chứa mã nguồn không thay đổi, và những lần commit có liên quan sẽ được nhóm lại với nhau. === Quản Lý Các Nhánh === Liệt kê tất cả các nhánh bằng cách gõ: $ git branch Theo mặc định, bạn bắt đầu tại nhánh có tên ``master''. Một số người chủ trương rời bỏ nhánh ``master'' mà không động chạm gì đến nó và tạo các nhánh mới dành cho các chỉnh sửa của riêng mình. Các tùy chọn *-d* and *-m* cho phép bạn xóa hay di chuyển (đổi tên) các nhánh. Xem thêm *git help branch*. Nhánh ``master'' thông thường rất hữu dụng. Những người khác sẽ nghĩ rằng kho chứa của bạn có nhánh mang tên này, và nhánh đó chứa phiên bản chính thức của dự án của bạn. Mặc dù bạn có thể đổi tên hay xóa bỏ nhánh ``master'', nhưng bạn không nên làm như thế mà hãy tôn trọng thỏa thuận ngầm này. === Nhánh Tạm === Một lát sau bạn có lẽ nhận thức được rằng mình cần có các nhánh tạm thời vì các lý do như: mọi nhánh khác đơn thuần phục vụ cho việc ghi lại trạng thái hiện tại do vậy bạn có thể nhảy trở lại các trạng thái cũ hơn để mà sửa chữa các lỗi nghiêm trọng hay làm một cái gì đó. Điều này cũng tương tự như việc chuyển kênh trên TV một cách tạm thời để thấy chương trình khác đang chiếu cái gì. Nhưng thay vì chỉ cần nhấn vài cái nút, bạn phải tạo, ``checkout'', trộn và xóa nhánh tạm đó. May mắn thay, Git có cách ngắn gọn tiện lợi chẳng thua kém gì chiếc điều khiển từ xa của một chiếc TV: $ git stash Lệnh này ghi lại trạng thái hiện hành vào một vị trí tạm thời (một *stash*) và phục hồi lại trạng thái trước đó. Thư mục bạn đang làm việc xuất hiện chính xác như trước khi bạn chỉnh sửa, và bạn có thể sửa lỗi, pull về các thay đổi ngược dòng, và cứ như thế. Khi bạn muốn qua trở lại trạng thái đã được tạm giấu đi đó, hãy gõ: $ git stash apply # Bạn có thể sẽ phải giải quyết các xung đột có thể nảy sinh. Bạn có thể có nhiều trạng thái được tạm giấu đi, và vận dụng chúng theo nhiều cách khác nhau. Xem *git help stash* để biết thêm chi tiết. Bạn cũng có thể đoán được, Git duy trì các nhánh ở hậu trường để thực hiện việc này. === Làm Theo Cách Của Mình === Bạn có thể sẽ cảm thấy việc sử dụng nhánh phiền hà quá. Cuối cùng, *clone* có lẽ là lệnh nhanh nhất, và bạn có thể hoán chuyển giữa chúng với lệnh *cd* thay vì sử dụng lệnh riêng của Git. Ta thử xét đến các trình duyệt web. Tại sao việc hỗ trợ mở nhiều tab thì cũng tốt như mở trên nhiều cửa sổ khác nhau? Bởi vì cả hai điều này thể hiện tính đa dạng của quan điểm, phong cách sống. Một số người sử dụng lại thích chỉ giữ một cửa sổ trình duyệt được mở, và sử dụng các tab để hiển thị nhiều trang web một lúc. Những người khác có lẽ lại khăng khăng cực đoan cho rằng: mở trên nhiều cửa sổ khác nhau và chẳng cần tab nữa. Một nhóm khác lại thích cả hai cách trên. Việc tạo nhánh thì cũng giống như tạo các tab cho thư mục làm việc của bạn, còn việc nhân bản thì lại giống như việc mở một cửa sổ duyệt mới. Những việc này nhanh chóng và nội bộ, thế thì sao lại không thử nghiệm để tìm thấy cách nào thích hợp nhất cho mình? Git giúp bạn làm việc chính xác như bạn muốn. ��������������������������������������������������������������������������������������gitmagic-20160304/vi/secrets.txt��������������������������������������������������������������������0000644�0001750�0001750�00000042103�12666307504�015627� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Bí Quyết của Git == Chúng ta mổ xẻ để hiểu được làm thế nào mà Git có thể thi hành kỳ diệu như vậy. Tôi sẽ không thể nói quá chi tiết được. Nếu bạn muốn có được sự mô tả chỉ tiết thì hãy đọc http://schacon.github.com/git/user-manual.html[sổ tay hướng dẫn sử dụng Git]. === Tính Ẩn === Git làm việc có vẻ kín đáo? Chỉ cần nói riêng về việc sử dụng lệnh *commit* và *merge*, bạn có thể làm việc mà không cần biết đến sự tồn tại của hệ thống quản lý mã nguồn. Cho đến khi bạn cần nó, và cho đến khi bạn vui sướng vì Git đã trông coi mã nguồn cho bạn trong suốt thời gian qua. Các hệ thống quản lý mã nguồn khác ép buộc bạn luôn luôn phải tranh đấu với thói quan liêu. Quyền truy cập của các tập tin có thể là chỉ cho phép đọc trừ phi bạn nói rõ với máy chủ trung tâm là các tập tin nào bạn muốn chỉnh sửa. Tốc độ làm việc của phần lớn các lệnh sẽ tỷ lệ nghịch với số lượng người sử dụng. Mọi công việc sẽ đình trệ khi mạng máy tính hay máy chủ ngừng hoạt động. Đối lập với hạn chế trên, Git đơn giản giữ lịch sử của dự án của bạn tại thư mục `.git` trong thư mục làm việc của bạn. Đây là bản sao lịch sử của riêng bạn, do vậy bạn có thể làm việc không cần mạng cho đến khi cần truyền thông với những người khác. Bạn có toàn quyền quyết định với các tập tin của mình bởi vì Git có thể tạo lại trạng thái đã ghi lại từ `.git` bất kỳ lúc nào một cách dễ dàng. === Toàn Vẹn Dữ Liệu === Phần lớn mọi người sử dụng phương pháp mã hóa để giữ cho thông tin của mình không bị nhòm ngó, nhưng có thứ quan trọng không kém đó là giữ cho thông tin của mình được toàn vẹn. Chính việc sử dụng hàm băm mã hóa đã làm ngăn ngừa sự sai hỏng dữ liệu do rủi ro hay ác ý. Giá trị SHA1 có thể coi như là một số định danh 160-bit không trùng lắp cho mỗi chuỗi ký tự bạn dùng trong đời sống của mình. Trên thực tế nó còn làm được nhiều hơn thế: nó có thể thực hiện điều trên với mọi chuỗi ký tự mà mọi người có thể sử dụng trong suốt cuộc đời của mình. Bản thân giá trị SHA1 cũng là một chuỗi ký tự, chúng ta có thể băm chuỗi có chứa giá trị băm khác. Khả năng quan sát đơn giản này cực kỳ hữu dụng: tra cứu 'hash chains' (tra cứu theo các chuỗi móc xích với nhau bằng giá trị băm). Sau này chúng ta sẽ thấy làm cách nào Git sử dụng nó để mà đảm bảo tính toàn vẹn của dữ liệu. Tóm lại, Git lưu giữ dữ liệu của bạn trong thư mục con `.git/objects`, thay vì sử dụng tên tập tin như thông thường, bạn sẽ chỉ nhìn thấy ID của chúng. Bằng cách sử dụng ID để làm tên tập tin, cũng tốt như là cách sử dụng kỹ thuật lockfiles (khóa tập tin) và timestamp (theo dõi thời gian của tập tin), Git biến hệ thống tập tin thông thường bất kỳ nào trở thành một cơ sở dữ liệu hiệu quả và mạnh mẽ. === Thông Minh === Làm thể nào mà Git biết bạn đã đổi tên một tập tin, dù là bạn chẳng bao giờ đề cập đến điều này một cách rõ ràng? Chắc chắn rồi, bạn có lẽ đã chạy lệnh *git mv*, nhưng nó chính xác giống hệt như việc chạy lệnh *git rm* sau đó là lệnh *git add*. Git khám phá ra cách truy tìm các tập tin đã được đổi tên hay sao chép giữa các phiên bản liên tiếp. Trên thực tế, nó có thể tìm ra từng đoạn mã nguồn đã bị di chuyển hay sao chép giữa các tập tin! Dẫu cho nó không thể xử lý được mọi trường hợp, nó làm khá tốt, và đặc tính này luôn luôn được phát triển. Nếu nó không làm việc với bạn, hãy thử bật các tùy chọn dành cho việc phát hiện sự sao chép, và nên cất nhắc đến việc cập nhật. === Mục Lục === Với mọi tập tin được theo dõi, Git ghi lại các thông tin như là kích thước, thời gian tạo và lần cuối sửa đổi trong một tập tin được biết đến là một mục lục 'index'. Để xác định rõ một tập tin có bị thay đổi hay không, Git so sánh nó ở thời điểm hiện tại với phần lưu giữ trong bộ nhớ. Nếu chúng giống nhau, thế thì Git có thể bỏ qua việc đọc tập tin đó lại lần nữa. Bởi vì gọi lệnh ``stat'' nhanh hơn đáng kể so với đọc tập tin, nếu bạn chỉ chỉnh sửa vài tập tin, Git có thể cập nhật trạng thái của nó cực kỳ nhanh chóng. Chúng ta đã nói trước rằng mục lục (index) là vùng làm việc của trạng thái. Tại sao lại là một chùm tập tin stat vùng stage? Bởi vì lệnh add đặt các tập tin vào trong cơ sở dữ liệu của Git và cập nhật những stat này, trong lúc lệnh commit được thực hiện, mà không có tùy chọn, tạo ra một commit trên cơ sở chỉ trên các stat và các tập tin đã sẵn có trong cơ sở dữ liệu. === Nguồn Gốc của Git === http://lkml.org/lkml/2005/4/6/121[Linux Kernel Mailing List post] này miêu tả các sự kiện nối tiếp nhau về Git. Toàn bộ tuyến này chỉ dành cho các sử gia đam mê Git. === Đối tượng Cơ Sở Dữ Liệu === Mỗi một phiên bản của dữ liệu của bạn được giữ trong 'đối tượng cơ sở dữ liệu' (object database), mà nó nằm trong thư mục con `.git/objects`; cái khác nằm trong thư mục `.git/` lưu giữ ít dữ liệu hơn: mục lục, tên các nhánh, các thẻ tag, các tùy chọn cấu hình, nhật ký, vị trí hiện tại của head của lần commit, và những thứ tương tự như thế. Đối tượng cơ sở dữ liệu cho đến bây giờ vẫn là phần tử cơ bản xuất sắc nhất, và là cội nguồn sức mạnh của Git. Mỗi tập tin trong `.git/objects` là một 'đối tượng'. Ở đây có 3 loại đối tượng liên quan đến chúng ta: đối tượng nội dung tập tin 'blob', đối tượng cây 'tree', và đối tượng lần chuyển giao 'commit'. === Đối Tượng Blob === Đầu tiên, hãy tạo một tập tin bất kỳ. Đặt cho nó một cái tên, tên gì cũng được. Trong một thư mục rỗng: $ echo sweet > YOUR_FILENAME $ git init $ git add . $ find .git/objects -type f Bạn sẽ thấy +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. Làm sao mà tôi biết được tập tin khi không thấy tên của nó? Đó là bởi vì đó là giá trị băm SHA1 của: "blob" SP "6" NUL "sweet" LF là aa823728ea7d592acc69b36875a482cdf3fd5c8d, với SP là khoảng trắng, NUL là byte có giá trị bằng 0 và LF ký tự xuống dòng. Bạn có thể xác minh lại bằng lệnh sau: $ printf "blob 6\000sweet\n" | sha1sum Git sử dụng cách 'lấy nội dung để làm tên cho tập tin': tập tin không được lưu trữ như theo tên của chúng, mà bằng giá trị băm dữ liệu mà chúng chứa, trong tập tin chúng ta gọi là một 'đối tượng blob'. Chúng ta có thể nghĩ giá trị băm như là một định danh duy nhất cho nội dung của tập tin, do vậy ta có tên tập tin được định danh bởi nội dung của nó. Phần khởi đầu `blob 6` đơn thuần chỉ là phần đầu để thể hiện kiểu của đối tượng và độ dài của nó tính bằng byte; việc làm này làm đơn giản hóa việc vận hành bên trong Git. Đến đây tôi có thể dễ dàng đoán được bạn nghĩ gì. Tên của tập tin là không thích hợp: chỉ khi có dữ liệu bên trong được sử dụng để xây dựng nên đối tượng blob. Bạn có lẽ sẽ kinh ngạc với những gì xảy ra với các tập tin có cùng nội dung. Hãy thử thêm một bản sao một tập tin nào đó của bạn, với bất kỳ một cái tên nào cũng được. Nội dung của +.git/objects+ ở tại cùng một chỗ cho dù bạn thêm vào bao nhiêu lần đi chăng nữa. Git chỉ lưu giữ dữ liệu một lần duy nhất. Nhưng dẫu sao, các tập tin nằm trong +.git/objects+ đã bị nén lại theo chuẩn zlib do vậy bạn không thể xem chúng một cách trực tiếp được. Đọc chúng thông qua http://www.zlib.net/zpipe.c[zpipe -d], hay gõ: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d lệnh này trình bày đối tượng được cho ở dạng dễ đọc trên màn hình. === Đối Tượng Tree === Nhưng mà tên tập tin ở đâu? Chúng phải được lưu giữ ở đâu đó chứ. Git lấy tên tập tin trong quá trình commit: $ git commit # Gõ chú thích. $ find .git/objects -type f Bạn sẽ thấy ba đối tượng. Ở thời điểm này tôi không thể nói hai tập tin mới này là cái gì, hãy nghĩ nó là một phần của tên tập tin bạn đang xét. Chúng ta sẽ xuất phát từ giả định bạn chọn ``rose''. Nếu bạn không làm thế, bạn có thể viết lại lịch sử để làm cho nó giống như bạn đã làm thế: $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose' $ find .git/objects -type f Bây giờ bạn có lẽ nhìn thấy tập tin +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, bởi vì đây là giá trị băm SHA1 của nội dung của nó: "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Xác thực tập tin này chứa thông tin như trên bằng cách gõ: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch Với lệnh zpipe, ta có thể dễ dàng xác thực một giá trị băm: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum Sự thẩm tra giá trị băm thì rắc rối hơn thông qua lệnh cat-file bởi vì phần kết xuất của nó chứa nhiều thông tin hơn đối tượng tập tin thường không bị nén. Tập tin này là một đối tượng cây 'tree': một danh sách các hàng bao gồm kiểu tập tin, tên tập tin, và giá trị băm. Trong ví dụ của chúng ta, kiểu tập tin là 100644, điều này có nghĩa `rose` là tập tin bình thường, và giá trị băm là một đối tượng blob mà nó chứa nội dung của `rose'. Dạng tập tin khác có thể là tập tin thực thi, liên kết mềm hay các thư mục. Trong trường hợp cuối, giá trị băm sẽ chỉ đến đối tượng cây 'tree'. Nếu bạn đã chạy lệnh filter-branch, bạn sẽ có các đối tượng cũ bạn không cần đến sau này nữa. Mặc dù chúng sẽ bị loại bỏ một cách tự động một khi thời hạn chấm dứt đã đến, nhưng chúng ta sẽ xóa chúng ngay bây giờ theo cách dưới đây: $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune Với các dự án thật bạn nên tránh việc sử dụng lệnh như trên, làm như thế bạn đã phá hủy dữ liệu sao lưu dự phòng. Nếu bạn muốn làm sạch kho chứa, cách hay nhất là tạo một bản sao mới. Cũng thế, hãy cẩn thận khi thao tác trực tiếp với thư mục +.git+: điều gì xảy ra nếu một lệnh Git khác cũng đang thực thi cùng lúc, hay là mất điện đột ngột? Đại khái, refs có thể được xóa bằng lệnh *git update-ref -d*, mặc dù thường thường nó an toàn hơn xóa +refs/original+ bằng tay. === Commit === Chúng tôi đã giảng giải cho bạn 2 trong số 3 đối tượng của Git. Cái thứ 3 chính là *commit*. Nội dung của nó buộc chặt vào phần chú thích của lần commit cũng như thời gian, ngày tháng chúng được tạo ra. Để cho khớp với những thứ chúng ta có ở đây, chúng ta sẽ phải chỉnh nó một chút: $ git commit --amend -m Shakespeare # Thay đổi phần chú thích. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # dấu vết thời gian và tác giả đã bị gian lận. $ find .git/objects -type f Bạn có thể thấy +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ là giá trị băm SHA1 của nội dung của nó: "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice <alice@example.com> 1234567890 -0800" LF "committer Bob <bob@example.com> 1234567890 -0800" LF LF "Shakespeare" LF Như ở phần trước, bạn có thể chạy lệnh zpipe hay cat-file để tự mình trông thấy. Đây là lần commit đầu tiên, do vậy lần commit này không có cha, nhưng những lần commit sau sẽ luôn luôn chứa it nhất là một dòng chỉ định commit cha. === Khó Phân Biệt Được sự Thần Kỳ === Bí mật của Git dường như có vẻ đơn giản. Nó giống như bạn có thể trộn lẫn cùng nhau một ít kịch bản và một ít mã C mà đun trong vài giờ: nhào trộn của quá trình hoạt động của hệ thống tập tin và mã băm SHA1, bày biện thêm với các khóa và đồng bộ hóa tập tin để tăng vị ngon. Trên thực tế, những mô tả như thế với Git các phiên bản trước kia là chính xác. Tuy nhiên, ngoài chiêu bài đóng gói để tiết kiệm không gian lưu trữ, sử dụng mục lục để tiết kiệm thời gian ra, giờ chúng ta còn biết thêm làm cách nào Git khéo léo thay đổi hệ thống tập tin thành một cơ sở dữ liệu hoàn hảo cho việc quản lý mã nguồn. Ví dụ, nếu một tập tin bất kỳ nào đó trong đối tượng cơ sở dữ liệu bị sai hỏng bởi lỗi do ổ đĩa, thế thì giá trị băm tương ứng của nó sẽ không đúng nữa, điều này sẽ mang lại rắc rối cho chúng ta. Bằng việc băm giá trị băm của đối tượng khác, chúng ta có thể duy trì tính toàn vẹn ở tất cả các mức. Commit là hạt nhân, thật vậy đấy, mỗi lần commit không bao giờ ghi lại nửa vời: chúng ta có thể chỉ tính toán mã băm của lần commit và lưu giữ giá trị của nó trong cơ sở dữ liệu sau khi chúng ta đã sẵn sàng lưu tất cả các đối tượng là trees, blobs và cha của các lần commit thích hợp. Đối tượng cơ sở dữ liệu không bị ảnh hưởng bởi các sự cố ngắt quãng bất ngờ như là mất điện đột ngột chẳng hạn. Chúng ta có thể làm thất bại ngay cả những kẻ phá hoại ranh mãnh. Giả sử người nào đó lén lút sửa chữa nội dung của một tập tin trong một phiên bản cũ của dự án. Để giữ đối tượng cơ sở dữ liệu vẫn hoạt động tốt, họ đồng thời cũng phải thay đổi giá trị băm của đối tượng blob tương ứng vì lẽ rằng nó bây giờ đã khác trước. Điều đó có nghĩa là họ sẽ phải thay đổi giá trị băm của một đối tượng tree có liên quan đến tập tin, và việc chỉnh sửa giá trị băm của tất cả các đối tượng commit kéo theo như là tree, thêm nữa là các giá trị băm của toàn bộ các lần commit con cháu của nó. Cái này kéo theo giá trị băm của head tại kho trung tâm không giống với thứ đó tại kho chứa sai hỏng. Bằng cách theo dõi sự tương khớp giá trị băm chúng ta có thể xác định được tập tin bị sai hỏng, cũng như là lần commit nơi mà nó lần đầu bị hư hỏng. Nói ngắn gọn, dùng 20 byte để đại diện cho lần commit cuối là an toàn, việc cố tình sửa đổi nội dung một kho chứa Git là điều không thể thực hiện được. Đặc tính nào của Git là trứ danh nhất? Nhánh? Trộn? Hay Tags? Chỉ là chi tiết. Head hiện hành được giữ trong tập tin +.git/HEAD+, mà nó có chứa giá trị băm của một đối tượng commit. Giá trị băm được cập nhật trong quá trình commit cũng như là một số lệnh khác. Nhánh đại thể cũng tương tự: chúng là các tập tin trong thư mục +.git/refs/heads+. Tags cũng thế: chúng ở tại +.git/refs/tags+ nhưng chúng được cập nhật bởi các lệnh khác nhau. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/multiplayer.txt����������������������������������������������������������������0000644�0001750�0001750�00000030704�12666307504�016532� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Đa Người Dùng == Lúc đầu tôi sử dụng Git cho dự án riêng của mình mà tôi cũng là người duy nhất phát triển nó. Trong số những lệnh liên quan đến bản chất phân tán của Git, tôi chỉ cần lệnh *pull* và *clone* để giữ cùng một dự án nhưng ở những chỗ khác nhau. Sau đó tôi muốn mã nguồn của mình được phổ biến trên mạng bằng việc sử dụng Git, và bao gồm cả những thay đổi từ những người đóng góp. Tôi đã phải học cách làm thế nào để quản lý các dự án có nhiều người phát triển phần mềm ở khắp nơi trên toàn thế giới. May mắn thay, đây là sở trường của Git, và người ta có thể nói đây là điều sống còn của một hệ thống quản lý mã nguồn. === Tôi Là Ai? === Mỗi lần commit sẽ lưu giữ tên và địa chỉ thư điện tử, điều này có thể nhìn thấy bằng lệnh *git log*. Theo mặc định, Git sử dụng các trường để lưu giữ các cài đặt trong hệ thống của mình. Để cài đặt các thông tin cá nhân của mình vào, hãy gõ: $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com Bỏ qua cờ global để đặt những thông tin này chỉ sử dụng cho kho chứa hiện tại. === Git Thông Qua SSH, HTTP === Giả sử bạn có thể truy cập vào một máy chủ web qua SSH, nhưng Git lại chưa được cài đặt ở đây. Mặc dù không hiệu quả như giao thức nguyên bản của nó, nhưng Git vẫn có thể truyền thông thông qua HTTP. Tải về, dịch và cài Git bằng tài khoản của bạn, và tạo kho chứa tại thư mục chứa trang web của bạn: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update Với các phiên bản Git cũ, lệnh copy không thực hiện được và bạn phải chạy: $ chmod a+x hooks/post-update Từ giờ bạn có thể xuất bản mới nhất của mình thông qua SSH từ một bản sao bất kỳ: $ git push máy.chủ.web:/đường/dẫn/đến/proj.git master và mọi người có thể lấy dự án của bạn với lệnh: $ git clone http://máy.chủ.web/proj.git === Git Thông Qua Mọi Thứ === Bạn muốn đồng bộ hóa kho chứa nhưng lại không có máy chủ và cũng không có mạng? Bạn cần trong những trường hợp khẩn cấp? Chúng ta đã biết lệnh <<makinghistory, *git fast-export* và *git fast-import* có thể chuyển đổi một kho chứa thành một tập tin đơn và ngược lại>>. Chúng ta có thể chuyển qua chuyển lại như vậy để truyền kho Git đi thông qua bất kỳ phương tiện nào, nhưng có một công cụ hiệu quả hơn đó chính là *git bundle*. Người gửi tạo một 'bundle': $ git bundle create somefile HEAD sau đó gửi bundle, +somefile+, tới người cần bằng cách nào đó: thư điện tử, ổ đĩa USB, và bản in *xxd* và một bộ quét nhận dạng chữ OCR, đọc các bit thông qua điện thoại, tín hiệu khói, v.v.. Người nhận khôi phục lại các lần commit từ bundle nhận được bằng cách gõ: $ git pull somefile Bộ nhận thậm chí có thể làm được việc này từ một kho chứa rỗng. Mặc dù kích thước của nó, +somefile+ chứa các mục kho Git nguyên thủy. Trong các dự án lớn hơn, việc triệt tiêu lãng phí bằng cách chỉ bundle những thay đổi còn thiếu kho chứa khác. Ví dụ, giả sử lần commit ``1b6d...'' là lần commit gần nhất đã được chia sẻ giữa cả hai thành viên: $ git bundle create somefile HEAD ^1b6d Nếu phải làm việc này thường xuyên, một khó khăn là bạn không thể nhớ được chính xác lần commit tương ứng với lần gửi cuối. Trang trợ giúp sẽ gợi ý cho bạn cách sử dụng các thẻ (tag) để giải quyết vấn đề này. Ấy là, sau khi bạn gửi một bundle, thì hãy gõ: $ git tag -f lastbundle HEAD và tạo một bản bundles mới với: $ git bundle create newbundle HEAD ^lastbundle === Vá: Sự Thịnh Hành Toàn Cầu === Miếng vá được trình bày ở dạng văn bản để thể hiện các thay đổi của bạn, nó dễ dàng được đọc hiểu bởi con người cũng như là máy tính. Điều này mang lại cho chúng sức lôi cuốn toàn cầu. Bạn có thể gửi miếng vá qua thư điện tử cho những nhà phát triển phần mềm khác mà chẳng cần lo họ đang sử dụng hệ thống quản lý mã nguồn nào. Chừng nào mà độc giả của bạn có thể đọc được thư điện tử của mình thì họ còn có thể thấy được phần chỉnh sửa của bạn. Tương tự thế, về phía mình, những thứ bạn cần là có một địa chỉ thư điện tử: ở đây chẳng cần cài đặt kho chứa Git nào trên mạng. Sử dụng lại ví dụ từ chương đầu tiên: $ git diff 1b6d > my.patch đầu ra là một miếng vá mà bạn có thể dán vào một thư điện tử để trao đổi với người khác. Ở kho Git, gõ: $ git apply < my.patch để áp dụng miếng vá. Còn một hình thức định dạng khác nữa, tên và có lẽ cả chữ ký của tác giả cũng được ghi lại, tạo miếng vá tương ứng với một thời điểm chính xác trong quá khứ bằng cách gõ: $ git format-patch 1b6d Tệp tin lưu kết quả có thể chuyển cho lệnh *git-send-email*, hay có thể làm thủ công. Bạn cũng có thể chỉ định rõ một vùng commit: $ git format-patch 1b6d..HEAD^^ Ở phía người nhận cuối, ghi lại thư điện tử thành tập tin, sau đó chạy lệnh: $ git am < email.txt Lệnh này sẽ áp dụng cho miếng vá nhận được, đồng thời tạo ra một lần commit, bao gồm các thông tin như là tác giả. Với một chương trình đọc thư điện tử, bạn có thể sử dụng con chuột để chuyển định dạng thư về dạng văn bản thuần trước khi ghi miếng vá thành một tập tin. Có một số khác biệt nhỏ giữa các trình đọc đọc thư điện tử, nhưng nếu bạn sử dụng một trong số chúng, bạn hầu như chắc chắn là người mà có thể cấu hình chúng một cách dễ dàng mà chẳng cần phải đọc hướng dẫn sử dụng! === Rất tiếc! Tôi đã chuyển đi === Sau khi nhân bản kho chứa, việc chạy lệnh *git push* hay *git pull* sẽ tự động push tới hay pull từ URL gốc. Git đã làm điều này như thế nào? Bí mật nằm ở chỗ các tùy chọn config đã được tạo ra cùng với bản sao. Hãy xem thử: $ git config --list Tùy chọn +remote.origin.url+ sẽ lưu giữ URL; ``origin'' là cái tên được đặt cho kho nguồn. Với nhánh ``master'' theo như thường lệ, chúng ta có thể thay đổi hay xóa các tên này nhưng chẳng có lý do gì để phải làm như thế cả. Nếu kho chứa nguyên bản đã chuyển chỗ, chúng ta có thể cập nhật URL thông qua: $ git config remote.origin.url git://url.mới/proj.git Tùy chọn +branch.master.merge+ chỉ ra nhánh remote mặc định trong lệnh *git pull*. Trong suốt quá trình nhân bản, nó được đặt cho nhánh hiện hành của kho chứa mã nguồn, như thế cho dù HEAD của kho nguồn về sau có di chuyển đến một nhánh khác, lệnh pull sau này sẽ trung thành với nhánh nguyên gốc. Tùy chọn này chỉ áp dụng cho kho chứa chúng ta lần đầu tiên nhân bản từ, nó được ghi trong tùy chọn +branch.master.remote+. Nếu chúng ta pull từ kho chứa khác chúng ta phải chỉ đích xác tên nhánh mà chúng ta muốn: $ git pull git://example.com/other.git master Phần phía trên giải thích tại sao một số lệnh push và pull ví dụ của chúng ta lại không có tham số. === Nhánh Trên Mạng === Khi bạn nhân bản một kho chứa, bạn cũng đồng thời nhân bản tất cả các nhánh của nó. Bạn sẽ không nhận được cảnh báo này bởi vì Git không thông báo cho bạn: bạn phải hỏi mới có thể biết chính xác. Việc làm này giúp ngăn ngừa phiền phức do nhánh mạng gây ra cho các nhánh của bạn, và cũng làm cho Git dễ dàng hơn với người mới dùng. Ta liệt kê các nhánh bằng lệnh: $ git branch -r Bạn nhận được kết quả trông giống như thế này: origin/HEAD origin/master origin/experimental Những nhánh tương ứng và HEAD của kho chứa remote, và bạn có thể sử dụng trong các lệnh Git thông thường. Ví dụ như là, giả sử bạn làm nhiều lần commit, và muốn so sánh chúng với bản đã fetch cuối cùng. Bạn cũng có thể tìm kiếm trong tập tin log để có được giá trị băm SHA1 thích hợp, nhưng dễ dàng hơn việc gõ: $ git diff origin/HEAD Hay bạn có thể xem nhánh ``experimental'' đang làm gì: $ git log origin/experimental === Đa Máy chủ === Giả sử hai người cùng phát triển trên một sự án của chúng ta, và họ muốn giữ lại sự khác biệt trên cả hai. Chúng ta theo dõi hơn một kho chứa tại một thời điểm với lệnh: $ git remote add other git://example.com/some_repo.git $ git pull other some_branch Bây giờ chúng ta có thể trộn với nhánh của kho chứa thứ hai, và chúng ta dễ dàng truy cập tất cả các nhánh của tất cả các kho chứa: $ git diff origin/experimental^ other/some_branch~5 Nhưng chúng ta chỉ muốn so sánh sự khác nhau giữ chúng nhưng không áp dụng các thay đổi này với chúng ta? Nói cách khác, chúng ta khảo sát các nhánh của họ nhưng không thay đổi những gì đang có trong thư mục làm việc của mình. Thế thì thay vì pull, ta dùng lệnh: $ git fetch # Lấy về từ nguyên gốc, theo mặc định. $ git fetch other # Lấy về từ lập trình viên thứ hai. Lệnh này chỉ mang về phần lịch sử. Mặc dù thư mục làm việc vẫn còn nguyên chưa bị động đến, chúng ta có thể xét bất kỳ nhánh nào của bất kỳ kho chứa nào trong một lệnh Git bởi vì chúng ta bây giờ đang làm việc trên bản sao trên máy của mình. Giờ ta xét đến phần hậu trường, lệnh pull đơn giản là *fetch* sau đó *merge*. Thông thường chúng ta *pull* bởi vì chúng ta muốn trộn với lần commit cuối cùng sau khi fetch; việc làm này là một ngoại lệ đáng chú ý. Xem *git help remote* để biết cách làm thế nào để gỡ bỏ kho chứa trên mạng, bỏ qua các nhánh xác định, và những thứ khác nữa. === Sở Thích Riêng Của Tôi === Với dự án của mình, tôi thích những người cộng tác tạo các kho chứa ở nơi mà tôi có thể pull. Một số dịch vụ Git cho phép bạn đặt nhánh riêng của mình từ một dự án trên đó chỉ cần sử dụng chuột. Sau khi tôi fetch một cây (tree), tôi chạy lệnh Git để di chuyển và xem xét các thay đổi, với ý tưởng là để tổ chức và mô tả tốt hơn. Tôi trộn với các thay đổi của chính mình, và có thể sẽ sửa thêm chút ít. Sau khi đã hài lòng, tôi push nó lên kho chứa chính. Mặc dù tôi ít nhận được sự cộng tác, nhưng tôi tin rằng việc này sẽ thay đổi theo chiều hướng tốt lên. Hãy đọc http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[blog của Linus Torvalds]. Git thuận lợi trong việc tạo các miếng vá, cũng như là nó tiết kiệm công sức cho chúng ta trong việc chuyển đổi chúng thành những lần commit dành cho Git. Hơn thế nữa, Git lưu giữ các thông tin rất chi tiết như là ghi lại tên và địa chỉ thư điện tử của tác giả, cũng như là ngày tháng và thời gian, và nó cũng đòi hỏi tác giả phải mô tả về những thay đổi mà họ đã tạo ra. ������������������������������������������������������������gitmagic-20160304/vi/preface.txt��������������������������������������������������������������������0000644�0001750�0001750�00000012622�12666307504�015567� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������= Git Magic = Ben Lynn Tháng Tám, 2007 == Lời nói đầu == http://git-scm.com/[Git] là công cụ quản lý mã nguồn vạn năng. Đây là một công cụ quản lý mã nguồn tin cậy, ổn định, đa dụng và cực kỳ mềm dẻo và chính sự mềm dẻo của Git làm cho việc học nó trở nên khó khăn, tất nhiên là không nói đến những người đã tạo ra nó. Theo quan sát của Arthur C. Clarke, bất kể công nghệ tiên tiến nào cũng không thể phân biệt rạch ròi là nó có kỳ diệu hay không. Đây cũng là cách hay đề đề cập đến Git: những người mới sử dụng không cần quan tâm đến bên trong Git làm việc như thế nào mà hãy xem khả năng thần kỳ của nó như là một điệu gizmo có thể làm những người coi nó là bạn sửng sốt và làm điên đầu những người đối lập. Thay vì đi sâu vào chi tiết, chúng tôi đưa ra phác thảo cách làm việc của các hiệu ứng chuyên biệt. Sau khi sử dụng lặp lại nhiều lần, từ từ bạn sẽ hiểu từng mẹo một và thực hiện được những việc mà mình muốn làm. .Bản dịch - link:/\~blynn/gitmagic/intl/zh_cn/[Tiếng Trung Giản thể]: dịch bởi JunJie, Meng và JiangWei. Đã chuyển đổi sang: link:/~blynn/gitmagic/intl/zh_tw/[Tiếng Trung Phồn thể] thông qua lệnh +cconv -f UTF8-CN -t UTF8-TW+. - link:/~blynn/gitmagic/intl/fr/[Tiếng Pháp]: dịch bởi Alexandre Garel; và đồng thời được xuất bản tại http://tutoriels.itaapy.com/[itaapy]. - link:/~blynn/gitmagic/intl/de/[Tiếng Đức]: dịch bởi Benjamin Bellee và Armin Stebich; và đồng thời xuất bản http://gitmagic.lordofbikes.de/[trên website của Armin]. - link:/~blynn/gitmagic/intl/it/[Tiếng Ý]: dịch bởi Mattia Rigotti. - link:/~blynn/gitmagic/intl/ko/[Tiếng Hàn Quốc]: dịch bởi Jung-Ho (John) Han; đồng thời https://sites.google.com/site/drinkhanjohn/useful-links/[xuất bản trên trang web của John]. - link:/~blynn/gitmagic/intl/pl/[Tiếng Ba Lan]: dịch bởi Damian Michna. - link:/~blynn/gitmagic/intl/pt_br/[Tiếng Bồ Đào Nha Bra-xin]: dịch bởi José Inácio Serafini và Leonardo Siqueira Rodrigues. - link:/~blynn/gitmagic/intl/ru/[Tiếng Nga]: dịch bởi Tikhon Tarnavsky, Mikhail Dymskov và một số người khác. - link:/~blynn/gitmagic/intl/es/[Tiếng Tây Ban Nha]: dịch bởi Rodrigo Toledo và Ariset Llerena Tapia. - link:/~blynn/gitmagic/intl/uk/[Ukrainian]: dịch bởi Volodymyr Bodenchuk. - link:/~blynn/gitmagic/intl/vi/[Tiếng Việt]: dịch bởi Trần Ngọc Quân và đồng thời xuất bản bản dịch này trên http://vnwildman.users.sourceforge.net/gitmagic/[trang Web cá nhân của mình]. .Các định dạng khác - link:book.html[Trang web đơn]: dạng HTML đơn giản, không được định dạng bằng CSS. - link:book.pdf[Định dạng PDF]: thuận tiện cho việc in ấn. - http://packages.debian.org/gitmagic[Gói dành cho Debian], http://packages.ubuntu.com/gitmagic[gói dành cho Ubuntu]: tải về đĩa cứng từ các địa chỉ này. Tiện lợi http://csdcf.stanford.edu/status/[khi máy chủ này không kết nối mạng hay không hoạt động]. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Sách giấy [Amazon.com]]: 64 trang, 15.24cm x 22.86cm, đen trắng. Rất tiện sử dụng vì chẳng cần đến điện. === Lời cảm ơn! === Tôi gửi lời cảm ơn đến những người đã dịch quyển sách này. Tôi rất cảm kích vì có được số lượng độc giả rộng lớn có được bởi những người đã được nêu tên ở trên. Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, Tyler Breisacher, Sonia Hamilton, Julian Haagsma, Romain Lespinasse, Sergey Litvinov, Oliver Ferrigni, David Toca, Сергей Сергеев, Joël Thieffry và Baiju Muthukadan đã đóng góp trong việc sửa chữa và cải tiến nội dung. François Marier đã bảo trì gói Debian do Daniel Baumann khởi xướng. Tôi cũng gửi lời cảm ơn tới sự giúp đỡ và sự tán dương của các bạn. Tôi muốn trích dẫn những lời đó ra đây, nhưng làm như thế có vẻ hơi lố bịch, tự cao tự đại. Nếu tôi có sai sót gì, xin hãy thông tin hay gửi bản vá cho tôi! === Giấy phép sử dụng === Hướng dẫn này được phát hành dựa trên http://www.gnu.org/licenses/gpl-3.0.html[Giấy Ghép Công phiên bản 3]. Đương nhiên, nội dung của quyển sách được quản lý bằng Git, và bạn có thể dễ dàng có được nó bằng cách gõ: $ git clone git://repo.or.cz/gitmagic.git # Tạo ra thư mục "gitmagic". hay từ các máy chủ khác: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git $ git clone https://code.google.com/p/gitmagic/ $ git clone git://git.assembla.com/gitmagic.git $ git clone git@bitbucket.org:blynn/gitmagic.git GitHub, Assembla, và Bitbucket có hỗ trợ các kho có tính riêng tư, hai địa sau là miễn phí. ��������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/basic.txt����������������������������������������������������������������������0000644�0001750�0001750�00000024510�12666307504�015242� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Các Thủ Thuật Cơ Bản == Thay vì lao vào cả một biển lệnh với Git, bạn hãy sử dụng các ví dụ cơ bản để bắt đầu. Mặc dù chúng rất đơn giản, nhưng tất cả chúng đều rất hữu dụng. Quả thực là vậy, trong tháng đầu tiên sử dụng Git, tôi chưa bao giờ vượt quá những gì nói trong chương này. === Ghi lại Trạng thái === Bạn muốn thử thực hiện một số lệnh gì đó với Git? Trước khi làm điều đó, thực hiện các lệnh sau trong thư mục hiện hành chứa các mã nguồn hay văn bản mà bạn muốn quản lý: $ git init $ git add . $ git commit -m "Bản sao lưu đầu tiên" Bây giờ nếu như các sửa đổi của bạn vừa làm không được như mong đợi, hãy phục hồi lại bản cũ: $ git reset --hard # Đặt lại trạng thái và dữ liệu như lần chuyển giao cuối Sau đó sửa nội dung cho đúng ý mình rồi ghi lại thành một trạng thái mới: $ git commit -a -m "Bản sao lưu khác" === Thêm, Xóa, Đổi Tên === Lệnh ở trên chỉ giữ dấu vết các tập tin hiện diện tại thời điểm bạn chạy lệnh *git add*. Nếu bạn thêm các tập tin hay thư mục, thì bạn sẽ phải thông báo với Git: $ git add readme.txt Documentation Tương tự như vậy, nếu bạn muốn Git bỏ đi các tập tin nào đó: $ git rm kludge.h obsolete.c $ git rm -r incriminating/evidence/ #gỡ bỏ một cách đệ qui Git xóa bỏ những tập tin nếu như bạn chưa làm. Đổi tên tập tin thì cũng giống như là việc bạn gỡ bỏ tên cũ và đặt vào nó cái tên mới. Lệnh *git mv* có cú pháp rất giống lệnh *mv* của hệ thống Linux. Ví dụ: $ git mv bug.c feature.c === Chức Năng Undo/Redo === Đôi khi bạn chỉ muốn quay trở lại và bỏ đi những thay đổi trong quá khứ tại một thời điểm nào đó bởi vì chúng tất cả đã sai. Thì lệnh: $ git log sẽ hiển thị cho bạn danh sách các lần chuyển giao gần đây cùng với giá trị băm SHA1: ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob <bob@example.com> Date: Tue Mar 14 01:59:26 2000 -0800 Replace printf() with write(). commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alice <alice@example.com> Date: Thu Jan 1 00:00:00 1970 +0000 Initial commit. ---------------------------------- Chỉ vài ký tự của giá trị băm là đủ để chỉ ra một chuyển giao cụ thể; một cách khác là chép và dán giá trị băm. Gõ: $ git reset --hard 766f để phục hồi lại trạng thái đã được chỉ ra và xóa bỏ tất cả các lần chuyển giao mới hơn kể từ đó. Một lúc nào đó bạn lại muốn nhảy tới một bản cũ hơn. Trong trường hợp này thì gõ: $ git checkout 82f5 Nó giúp bạn quay lại đúng thời điểm đó, trong khi vẫn giữ lại những lần chuyển giao mới hơn. Tuy nhiên, giống như cỗ máy thời gian trong các bộ phim khoa học viễn tưởng, nếu bây giờ bạn sửa sau đó chuyển giao, bạn sẽ ở trong một thực tại khác, bởi vì hành động của bạn bây giờ đã khác với khi chúng ta lần đầu tiên ở tại đây. Có cách thực tế hơn là sử dụng 'branch', và <<branch, chúng ta có nhiều điều để nói về nó sau này>>. Bây giờ, chỉ cần nhớ là: $ git checkout master sẽ mang chúng ta trở về hiện tại. Ngoài ra, để tránh rủi ro khi sử dụng Git, thì luôn luôn chuyển giao hay reset các thay đổi của bạn trước khi chạy lệnh checkout. Sự tương đồng với *ván đấu* trên máy tính: - *`git reset --hard`*: lấy cái cũ đã được lưu lại và xóa tất cả các *ván đấu* mới hơn cái vừa lấy. - *`git checkout`*: lấy một cái cũ, nhưng chỉ chơi với nó, trạng thái của *ván đấu* sẽ tách riêng về phía mới hơn chỗ mà bạn đã ghi lại lần đầu tiên. Bất kỳ *ván đấu* nào bạn tạo từ bây giờ sẽ là bản cuối cùng trong nhánh riêng rẽ tương ứng với một thực tại khác mà bạn đã gia nhập vào. <<branch, chúng tôi sẽ nói sau>>. Bạn có thể chọn chỉ phục hồi lại các tập tin hay thư mục bạn muốn bằng cách thêm vào chúng vào phần sau của câu lệnh: $ git checkout 82f5 some.file another.file Bạn phải cẩn thận khi sử dụng các lệnh, như là lệnh *checkout* có thể âm thầm ghi đè lên các tập tin. Để ngăn ngừa rủi ro như thế, hãy chuyển giao trước khi chạy lệnh checkout, nhất là khi mới học sử dụng Git. Tóm lại, bất kỳ khi nào bạn không chắc chắn về một lệnh nào đó, dù có là lệnh của Git hay không, đầu tiên hãy chạy lệnh *git commit -a*. Bạn không thích việc cắt dán ư? Hãy sử dụng: $ git checkout :/"My first b" để nhảy tới lần chuyển giao mà phần chú thích của nó bắt đầu với chuỗi bạn đã cho. Bạn cũng có thể yêu cầu trạng thái thứ 5 kể từ cái cuối cùng: $ git checkout master~5 === Sự quay lại === Trong một phiên tòa, mỗi sự kiện được gắn với một bản ghi. Cũng giống thế, bạn có thể chọn lệnh chuyển giao để undo. $ git commit -a $ git revert 1b6d sẽ chỉ undo lần chuyển giao với giá trị băm đã chỉ ra. Sự quay trở lại được ghi nhận như là một lần chuyển giao mới, bạn có thể xác nhận lại điều này bằng lệnh *git log*. === Tạo Nhật Ký các thay đổi === Một số dự án yêu cầu phải có một http://en.wikipedia.org/wiki/Changelog[changelog]. Bạn có thể tạo một cái bằng cách gõ: $ git log > Changelog # ThayĐổi === Tải về các tập tin === Lấy về một bản sao của một dự án quản lý bằng Git bằng cách gõ: $ git clone git://server/path/to/files Ví dụ, để lấy tất cả các tập tin mà tôi đã dùng để tạo ra cho quyển sách này là: $ git clone git://git.or.cz/gitmagic.git Chúng ta sẽ có nhiều điều để nói về lệnh *clone* sớm thôi. === Thử Nghiệm === Nếu bạn đã tải về một bản sao của một dự án bằng lệnh *git clone*, bạn có thể lấy về phiên bản cuối cùng với lệnh: $ git pull === Xuất Bản === Giả sử bạn đã tạo được một kho Git và bạn muốn chia sẻ nó với người khác. Bạn có thể bảo họ tải về từ máy tính của mình, nhưng nếu họ làm như thế trong khi bạn đang cải tiến nó hay có những thay đổi mang tính thử nghiệm, họ có thể gặp trục trặc. Dĩ nhiên, đây là lý do tại sao mà chu kỳ phát hành phần mềm lại tồn tại phải không nào. Những người phát triển có thể làm việc thường xuyên trên một dự án, nhưng họ chỉ xuất bản những đoạn mã mà họ cảm thấy nó có thể dùng được để tránh ảnh hưởng đến người khác. Thực hiện điều này với Git, trong thư mục làm việc của Git: $ git init $ git add . $ git commit -m "Bản phát hành đầu tiên" Sau đó nói với những người cùng sử dụng hãy chạy: $ git clone máy.tính.của.bạn:/đường/dẫn/tới/script để tải dữ liệu về. Giả định là họ truy cập thông qua ssh. Nếu không, chạy *git daemon* và nói với người sử dụng là chạy lệnh sau để thay thế: $ git clone git://máy.tính.của.bạn:/đường/dẫn/tới/script Kể từ lúc này, bất cứ khi nào mã nguồn của bạn đã có thể sử dụng được, chỉ việc thực hiện: $ git commit -a -m "Bản phát hành tiếp" và những người sử dụng có thể cập nhật dữ liệu của họ bằng cách chuyển tới thư mục làm việc tương ứng và gõ: $ git pull Những người sử dụng sẽ không bao giờ thấy được dữ liệu cuối cùng của bạn mà bạn không muốn họ thấy. === Tôi Đã Làm Được Gì? === Tìm tất cả các thay đổi kề từ lần bạn chuyển giao lần cuối bằng lệnh: $ git diff Hay từ hôm qua: $ git diff "@{yesterday}" Hay giữa một bản nào đó và bản trước đây 2 bản: $ git diff 1b6d "master~2" Trong từng trường hợp, đầu ra là một miếng vá mà nó có thể được sử dụng với lệnh *git apply*. Cũng có thể dùng lệnh: $ git whatchanged --since="2 weeks ago" Thường thường, tôi duyệt lịch sử bằng http://sourceforge.net/projects/qgit[qgit] để thay thế cách ở trên, bởi vì nó có giao diện đồ họa bóng bẩy, hay http://jonas.nitro.dk/tig/[tig], có giao diện dòng lệnh làm việc rất tốt với các máy có kết nối mạng chậm. Một lựa chọn khác là cài đặt máy chủ web, chạy lệnh *git instaweb* và sử dụng bất kỳ trình duyệt web nào. === Bài Tập === Giả sử A, B, C, D là 4 lần chuyển giao thành công, với B giống A ngoại trừ một số tập tin bị xóa bỏ. Chúng ta muốn thêm các tập tin đó trở lại D. Chúng ta thực hiện điều này bằng cách nào? Ở đây chúng ta có ít nhất 3 giải pháp. Giả thiết chúng ta đang ở D: 1. Sự khác nhau giữa A và B là việc các tập tin đã bị gỡ bỏ. Chúng ta có thể tạo miếng vá tương ứng với sự khác biệt này và áp dụng miếng vá đó: $ git diff B A | git apply 2. Kể từ sau khi chúng ta ghi lại các tập tin tại A trở đi, chúng ta có thể lấy lại: $ git checkout A foo.c bar.h 3. Chúng ta có thể xem sự di chuyển từ A tới B giống như là một thay đổi mà chúng ta muốn undo: $ git revert B Lựa chọn nào là tốt nhất? Cách nào bạn thích nhất. Thật dễ dàng để có được thứ mà bạn muốn với Git, và thường là có nhiều hơn một cách để thực hiện được một thứ bạn muốn. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/grandmaster.txt����������������������������������������������������������������0000644�0001750�0001750�00000034442�12666307504�016475� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Trở Thành Kiện Tướng == Bây giờ, bạn có thể thông qua lệnh *git help* để bật trang trợ giúp lên và có thể hiểu gần như tất cả mọi thứ. Tuy nhiên, việc xác định chính xác lệnh cần sử dụng để giải quyết các vấn đề thực tế đặt ra có lẽ chẳng dễ dàng gì. Có thể tôi có thể giúp bạn tiết kiệm được thời gian: bên dưới là một vài cách giải quyết các vấn đề thực tế đặt ra mà tôi đã từng sử dụng trong quá khứ. === Phát hành Mã Nguồn === Với dự án của tôi, Git giữ theo dõi chính xác các tập tin tôi muốn lưu trữ và phát hành tới người dùng. Để tạo gói tarball cho mã nguồn, tôi chạy: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === Chỉ Commit Những Gì Thay Đổi === Việc phải thông báo với Git khi bạn thêm, xóa hay đổi tên các tập tin là việc rầy rà với các dự án nào đó. Thay vào đó, bạn có thể gõ: $ git add . $ git add -u Git sẽ xem tất cả các tập tin trong thư mục hiện tại và làm công việc mà nó phải làm. Thay vì chạy lệnh add thứ hai, hãy chạy `git commit -a` nếu bạn cũng có ý định commit vào lúc này. Xem *git help ignore* để biết làm cách nào để chỉ ra các tập tin bỏ qua. Bạn có thể thi hành những điều trên chỉ cần một dòng lệnh: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove Tùy chọn *-z* và *-0* dùng để ngăn ngừa sai hỏng không mong muốn từ những tập tin có chứa các ký tự đặc biệt. Bởi vì lệnh này bổ xung những tập tin đã bị bỏ qua, bạn có thể muốn sử dụng tùy chọn `-x` hay `-X`. === Lần commit này Nhiều Quá! === Bạn quên việc commit quá lâu? Bạn quá mải mê với việc viết mã nguồn mà quên đi việc quản lý nó? Bạn muốn những thay đổi liên quan đến nhau phải được commit riêng từng lần và nối tiếp nhau, bởi vì đây là phong cách của bạn? Đừng lo lắng. Chạy: $ git add -p Với mỗi lần thay đổi mà bạn tạo ra, Git sẽ hiện cho bạn biết từng đoạn mã đã bị thay đổi, và hỏi nó có phải là một bộ phận của lần commit tiếp theo. Trả lời là "y" hay "n". Bạn có các sự lựa chọn khác, như là hoãn lại; gõ "?" để biết thêm chi tiết. Khi nào bạn thỏa mãn thì gõ $ git commit để commit chính xác các thay đổi mà bạn đã chọn lựa (các thay đổi về 'staged'). Hãy chắc chắn là bạn đã không dùng tùy chọn *-a*, nếu không thì Git sẽ commit tất cả. Nhưng bạn lại có những tài liệu đã được chỉnh sửa đặt tại nhiều chỗ khác nhau? Việc duyệt từng cái một sẽ làm bạn nản lòng. Trong trường hợp này, sử dụng lệnh *git add -i*, với giao diện không ít rắc rối hơn, nhưng uyển chuyển hơn. Chỉ cần gõ vài cái, bạn có thể đưa vào hay gỡ bỏ nhiều tập tin vào một trạng thái cùng một lúc, hay xem xét và chọn các thay đổi chỉ trong các tập tin riêng biệt. Có một sự lựa chọn khác, chạy lệnh *git commit \--interactive* mà nó sẽ tự động commit sau khi bạn làm xong. === Mục Lục: Vùng trạng thái của Git === Chúng ta trốn tránh chưa muốn nói đến một thứ nổi tiếng của Git đó là 'index' (mục lục), nhưng chúng ta phải đối mặt với nó để mà giảng giải những điều ở trên. Chỉ mục là vùng trạng thái tạm thời. Git ít khi di chuyển dữ liệu qua lại một cách trực tiếp giữa dự án của bạn và lịch sử của nó. Đúng hơn là Git đầu tiên ghi dữ liệu vào mục lục, và sau đó sao chép dữ liệu trong chỉ mục vào chỗ cần ghi cuối. Ví dụ, lệnh *commit -a* thực sự bao gồm hai quá trình. Bước thứ nhất là đặt toàn bộ dữ liệu hiện tại của mọi tập tin cần theo dõi vào bảng mục lục. Bước thứ hai là ghi vào bảng mục lục. Việc commit không sử dụng tùy chọn *-a* chỉ thi hành bước thứ hai, và nó chỉ có ý nghĩa sau khi chạy lệnh mà lệnh này bằng cách này hay cách khác thay đổi bảng chỉ mục, như là lệnh *git add* chẳng hạn. Thường thường chúng ta bỏ qua mục lục và lấy cớ là chúng ta đang đọc trực tiếp và ghi thẳng vào trong lịch sử. Vì lý do này, chúng ta muốn việc điều khiển chính xác, như vậy chúng ta chỉnh sửa mục lục bằng cách thủ công. Chúng ta đặt một dữ liệu hiện hành của một số, không phải tất cả, các thay đổi của chúng ta vào bảng mục lục, và sau đó ghi những cái này vào lịch sử. === Đừng Quên HEAD Của Mình === Thẻ HEAD giống như một con trỏ, nó trỏ đến lần commit cuối cùng, tự động di chuyển theo mỗi lần commit mới. Một số lệnh của Git giúp bạn di chuyển nó. Ví dụ như: $ git reset HEAD~3 sẽ chuyển HEAD lên vị trí lần commit cách đây ba lần. Thế thì tất cả các lệnh Git thi hành như khi bạn ở vị trí commit này, trong khi các tập tin của bạn vẫn nằm ở hiện tại. Xem thêm phần trợ giúp cho một số ứng dụng. Nhưng ta lại muốn quay trở lại phần sau này? Lần commit cũ không biết gì về phần sau này cả. Nếu bạn có giá trị băm SHA1 của HEAD gốc thì: $ git reset 1b6d Nhưng giả sử bạn không có được nó? Đừng lo: với những lệnh như thế, Git ghi lại HEAD gốc với thẻ có tên là ORIG_HEAD, và bạn có thể trở về ngon lành và an toàn với lệnh: $ git reset ORIG_HEAD === Tìm HEAD === Có thể ORIG_HEAD là chưa đủ. Có lẽ bạn vừa nhận thấy mình vừa tạo ra một sai sót có quy mô lớn và bạn cần phải quay lại một lần commit cách đây lâu lắm rồi trong một nhánh mà bạn đã quên rồi vì nó đã quá lâu. Theo mặc định, Git giữ một lần commit ít nhất là hai tuần lễ, ngay cả khi bạn đã ra lệnh cho Git phá hủy nhánh chứa nó. Sự khó khăn là ở chỗ làm thế nào để tìm được giá trị băm thích hợp. Bạn có thể tìm kiếm tất cả các giá trị băm trong `.git/objects` và sử dụng phương pháp thử sai tất cả các giá trị để có được thứ mình muốn. Nhưng còn có cách dễ dàng hơn. Git ghi lại mọi giá trị băm của mọi lần commit trong máy tính tại thư mục `.git/logs`. Thư mục con `refs` chứa lịch sử của tất cả các hoạt động trên tất cả cách nhánh, trong khi tập tin `HEAD` giữ tất cả các giá trị băm mà nó từng có được. Phần sau có thể được sử dụng để tìm kiếm giá trị băm của các lần commits trên các nhánh cái mà đã bị cắt đi một cách tình cờ. Lệnh reflog cung cấp cho chúng ta một giao diện thân thiện dành cho các tập tin log. Bạn có thể thử bằng lệnh: $ git reflog Thay vì phải cắt và dán giá trị băm từ reflog, hãy thử: $ git checkout "@{10 minutes ago}" Hay checkout lần thứ 5 kể từ lần commit cuối viếng thăm thông qua lệnh: $ git checkout "@{5}" Xem chương ``Specifying Revisions'' (tạm dịch: chỉ định các điểm xét duyệt) từ lệnh *git help rev-parse* để biết thêm chi tiết. Bạn muốn cấu hình thời gian gia hạn lâu hơn việc xóa bỏ những lần commit. Ví dụ: $ git config gc.pruneexpire "30 days" có nghĩa là việc xóa một lần commit sẽ chỉ thực sự xảy ra khi 30 ngày đã qua và lệnh *git gc* được triệu gọi. Bạn cũng có thể không cho phép chạy lệnh *git gc* một cách tự động: $ git config gc.auto 0 trong trường hợp này những lần commit sẽ chỉ bị xóa bỏ khi bạn chạy lệnh *git gc*. === Xây Dựng trên Git === Tuân thủ theo phong thái UNIX, Git được thiết kế cho phép nó dễ dàng được sử dụng như là một thành phần thực thi bên dưới của các chương trình khác, như là cho giao diện đồ họa GUI và giao diện Web để thay thế cho giao diện dòng lệnh, công cụ quản lý các miếng vá, các công cụ xuất/nhập và chuyển đổi, và những thứ tương tự như thế. Trên thực tế, một số lệnh Git bản chất nó cũng là các kịch bản đứng trên vai của những người khổng lồ, chính là hệ điều hành. Chỉ cần sửa đổi một chút, bạn có thể bắt Git làm việc phù hợp với sở thích của mình. Một mẹo nhỏ là sử dụng một tính năng sẵn có trong Git bằng cách gán bí danh cho các lệnh để nó trở nên ngắn gọn hơn như sau: $ git config --global alias.co checkout # gán bí danh cho lệnh checkout là co $ git config --global --get-regexp alias # hiển thị bí danh hiện hành alias.co checkout $ git co foo # có kết quả giống như chạy lệnh 'git checkout foo' Một thứ khác là hiển thị nhánh hiện hành lên màn hình hay thanh tiêu đề của cửa sổ. Gọi lệnh: $ git symbolic-ref HEAD sẽ hiển thị tên của nhánh hiện hành. Trong thực tiễn, bạn thích hợp nhất muốn gỡ bỏ "refs/heads/" và tránh các lỗi: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- Thư mục con +contrib+ là một kho báu được tìm thấy trong số các công cụ được xây dựng dành cho Git. Đúng lúc, một số trong số chúng có thể được xúc tiến thành lệnh chính thức. Trên hệ thống Debian và Ubuntu, thư mục này ở tại +/usr/share/doc/git-core/contrib+. Một thư mục phổ biến khác là +workdir/git-new-workdir+. Thông qua các liên kết tài tình, đoạn kịch bản này tạo ra một thư mục làm việc mới trong khi phần lịch sử thì chia sẻ với kho chứa nguyên gốc: $ git-new-workdir an/existing/repo new/directory Thư mục mới và các tập tin trong nó có thể được coi như một bản sao, ngoại trừ phần lịch sử được chia sẻ dùng chung, hai cây được tự động đồng bộ hóa. Ở đây không cần có sự trộn, push, hay pull. === Cứ Phiêu Lưu === Ngày nay, người sử dụng Git rất khó phá hủy dữ liệu. Nhưng nếu như bạn biết mình đang làm gì, bạn có thể vượt qua sự bảo vệ dành cho các lệnh thông thường đó. *Checkout*: Không commit các thay đổi là nguyên nhân của việc checkout gặp lỗi. Để phá hủy sự thay đổi của mình, và dẫu sao cũng checkout commit đã cho, sử dụng cờ bắt buộc (force): $ git checkout -f HEAD^ Mặt khác, nếu bạn chỉ định rõ một đường dẫn chi tiết cho lệnh, thế thì ở đây không có sự kiểm tra an toàn nào cả. Đường dẫn được áp dụng sẽ bị âm thầm ghi đè lên. Hãy cẩn thận nếu bạn sử dụng lệnh checkout theo cách này. *Reset*: Lệnh reset cũng xảy ra lỗi khi không commit các thay đổi. Để bắt buộc nó, chạy: $ git reset --hard 1b6d *Branch*: Việc xóa các nhánh cũng gặp lỗi nếu đó là nguyên nhân khiến các thay đổi bị mất. Để ép buộc việc này, hãy gõ: $ git branch -D dead_branch #thay vì sử dụng tùy chọn -d Cũng tương tự như thế, việc cố gắng ghi đè lên một nhánh bằng cách di chuyển nhánh khác đến nó cũng gây ra lỗi. Để ép buộc sự di chuyển nhánh, gõ: $ git branch -M source target # thay vì sử dụng tùy chọn -m Không giống như checkout và reset, hai lệnh trên trì hoãn việc phá hủy dữ liệu. Các thay đổi vẫn còn lưu giữ trong thư mục con .git, và có thể lấy lại được bằng cách lấy giá trị băm `.git/logs` thích hợp (xem phần "Tìm - HEAD" ở phía trên). Theo mặc định, chúng sẽ giữ ít nhất là hai tuần lễ. *Clean*: Một số lệnh Git từ chối thi hành bởi vì chúng lo lắng về việc làm như thế làm mất dấu hoàn toàn các tập tin. Nếu bạn chắc chắn về tất cả các tập tin và thư mục không cần Git theo dõi nữa và muốn phá hủy chúng đi, thế thì xóa chúng triệt để với lệnh: $ git clean -f -d Sau này, lệnh rầy rà đó sẽ hoạt động! === Ngăn Ngừa Commit Sai === Có một số lỗi ngớ ngẩn đã xảy ra với tôi. Điều tồi tệ nhất là để sót các tập tin bởi vì quên lệnh *git add*. Ít tệ hại hơn là các ký tự khoảng trắng và những xung đột không cần phải trộn: mặc dù cũng chẳng tệ hại lắm, tôi mong rằng những điều này sẽ không xảy ra với mọi người. Tôi đã tránh được các lỗi ngu ngốc đó bằng cách sử dụng một _hook_ để nó cảnh báo người dùng khi có những vấn đề: $ cd .git/hooks $ cp pre-commit.sample pre-commit # Với phiên bản Git cũ cần chạy lệnh: chmod +x pre-commit Ngày nay Git sẽ không commit nếu khi nó trộn nó chỉ tìm thấy những khoảng trắng vô ích hay những xung đột không cần giải trộn. Với bản hướng dẫn này, tôi cuối cùng đã thêm vào dòng đầu của hook *pre-commit* để đề phòng khi ta lơ đãng: if git ls-files -o | grep '\.txt$'; then echo FAIL! Untracked .txt files. exit 1 fi Nhiều hoạt động của Git hỗ trợ hook; hãy xem *git help hooks*. Chúng tôi đã kích hoạt một hook mẫu là *post-update* trước khi nói đến Git thông qua HTTP. Cái này chạy mỗi khi head di chuyển. Đoạn script ví dụ post-update cập nhật các tập tin Git cần cho việc truyền thông thông qua Git-agnostic chuyên chở bằng giao thức giống như là HTTP. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/history.txt��������������������������������������������������������������������0000644�0001750�0001750�00000037513�12666307504�015671� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Bài Học về Lịch Sử == Một hệ quả tất yếu của đặc tính phân tán của Git là việc lịch sử có thể biên soạn lại một cách dễ dàng. Nhưng nếu bạn xáo trộn quá khứ, hãy cẩn thận: chỉ biên soạn lại các phần trong lịch sử chỉ khi bạn sở hữu nó một mình. Cũng giống như việc các quốc gia tranh cãi không kết thúc xem ai là người tận tâm, hành động nào là tàn ác, nếu một người khác có một bản sao mà lịch sử của nó lại khác với cái của bạn, bạn sẽ gặp rắc rối ngay khi cần tương tác với họ. Một số nhà phát triển phần mềm quả quyết rằng lịch sử không thể thay đổi, tất cả mọi thứ. Một số khác lại cho rằng chỉnh sửa lại cấu trúc trước khi phát hành nó ra đại chúng. Git chấp nhận cả hai quan điểm. Giống như việc nhân bản, tạo nhánh và hòa trộn, viết lại lịch sử đơn giản chỉ là một quyền lực mà Git trao cho bạn. Bạn có thể làm thế nếu muốn. === Dừng Lại Sửa Chữa === Bạn vừa mới commit, nhưng lại ước rằng mình đã gõ những dòng chú thích có nội dung khác phải không? Thế thì hãy chạy: $ git commit --amend để thay đổi chú thích cuối cùng. Bạn giật mình vì quên thêm các tập tin vào? Chạy lệnh *git add* để thêm nó vào, và sau đó lại chạy lệnh ở trên. Bạn muốn thêm vài chỉnh sửa vào lần cuối mình đã commit ư? Thế thì cứ sửa chúng đi và sau đó chạy lệnh: $ git commit --amend -a === ... Và Sau đó là Nhiều Lần === Giả sử vấn đề trục trặc ở lần commit cách đây mười lần. Sau một buổi làm việc dài, bạn đã tạo ra hàng tá các lần commit. Nhưng bạn không hoàn toàn hài lòng với cách mà chúng được tổ chức, và một số lần commit cần được soạn lại phần mô tả. Thế thì hãy gõ: $ git rebase -i HEAD~10 và 10 lần commit cuối sẽ xuất hiện trong $EDITOR yêu thích của bạn. Dưới đây là một đoạn trích ví dụ: pick 5c6eb73 Added repo.or.cz link pick a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile Lần commit cũ đứng trước lần mới hơn trong danh sách, không giống như kết quả khi chạy lệnh `log`. Ở đây, 5c6eb73 là lần commit cũ nhất, và 100834f là mới nhất. Thế thì: - Xóa bỏ các lần commit bằng cách xóa các dòng tương ứng. Giống như lệnh revert, nhưng không ghi biên bản: nó sẽ coi như là lần commit đó chưa từng bao giờ tồn tại. - Đặt lại thứ tự các lần commit bằng cách thay đổi thứ tự các dòng. - Thay thế `pick` bằng: * `edit` để đánh dấu lần commit đó là dành cho việc tu bổ. * `reword` để thay đổi phần chú giải. * `squash` để hòa trộn với lần commit trước. * `fixup` để hòa trộn với lần commit trước và bỏ qua việc ghi lại phần chú giải. Ví dụ, chúng ta chẳng hạn thay thế `pick` ở dòng thứ hai bằng `squash`: pick 5c6eb73 Added repo.or.cz link squash a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile Sau đó chúng ta ghi lại thay đổi và thoát ra. Git trộn lần a311a64 vào 5c6eb73. Vì vậy *squash* trộn với lần kế trước nó: có thể nghĩ đây là quá trình ``nén dữ liệu''. Hơn thế nữa, Git sau đó tổ hợp nhật ký của chúng và hiện tại và chỉnh sửa lại. Lệnh *fixup* bỏ qua bước này; việc sửa nhật ký đơn giản là bỏ qua. Nếu bạn đánh dấu một lần commit bằng *edit*, Git đưa bạn trở lại quá khứ, tới lần commit lâu nhất đó. Bạn có thể tu bổ một lần commit cũ như đã mô tả ở phần trên, và thậm chí tạo ra các lần commit mới ở chỗ này. Một khi bạn đã hài lòng với việc ``retcon'', hãy chạy 'cỗ máy thời gian' bằng cách chạy lệnh: $ git rebase --continue Git sửa commits cho tới *edit* kế tiếp, hoặc tới hiện tại nếu không còn việc gì cần phải làm. Bạn còn có thể bãi bỏ việc rebase bằng lệnh: $ git rebase --abort Do vậy cứ commit thoải mái và thường xuyên bởi vì bạn có thể dọn dẹp cho gọn gàng sau này bằng lệnh rebase. === Thay Đổi Riêng Sắp Xếp Sau === Bạn đang làm việc trên một dự án đang hoạt động. Bạn đã tạo ra một số lần commit tại máy tính của mình, và sau đó bạn đồng bộ hóa với cây chính thức bằng cách hòa trộn. Chu kỳ này tự lặp chính nó một số lần trước khi bạn thực sự push tới cây trên máy chủ trung tâm. Nhưng hiện tại lịch sử bản sao Git trên máy tính của bạn là một mớ hỗn độn của những lần thay đổi trên máy tính riêng và máy chính thức. Bạn muốn thấy tất cả các thay đổi của riêng mình trong một đoạn liên tục không ngắt quãng, và sau tất cả các thay đổi từ kho chính thức. Đây chính là công việc dành cho lệnh *git rebase* đã được miêu tả ở trên. Trong nhiều trường hợp bạn có thể sử dụng cờ *--onto* và tránh xa sự tương tác với các máy tính khác. Xem thêm trong *git help rebase* để thấy được chi tiết các ví dụ dành cho lệnh đáng kinh ngạc này. Bạn có thể chia cắt các lần commit. Bạn còn có thể xắp xếp lại các nhánh của một cấu trúc cây. Hãy cẩn thận: rebase là một lệnh mạnh mẽ. Với những lần rebases phức tạp, trước hết hãy tạo ra một bản sao lưu dự phòng bằng lệnh *git clone*. === Viết Lại Lịch Sử === Thỉnh thoảng, bạn muốn việc quản lý mã nguồn giống việc người ta sơn vẽ chân dung một con người, tẩy xóa chúng từ lịch sử như theo ý của Stalinesque. Ví dụ, giả sử chúng ta có ý định phát hành dự án, nhưng lại liên đới đến một tập tin mà nó phải được giữ bí mật vì lý do nào đó. Chẳng hạn như tôi đã quên khi ghi lại số thẻ tín dụng vào trong một tập tin văn bản và ngẫu nhiên nó được thêm vào trong dự án. Việc xóa tập tin này là chưa đủ, bởi vì ta có thể đọc nó từ lần commit cũ. Chúng ta phải gỡ bỏ tập tin này từ tất cả các lần đã commit: $ git filter-branch --tree-filter 'rm top/secret/file' HEAD Xem *git help filter-branch*, nội dung của nó sẽ thảo luận về ví dụ này và đưa ra một cách thức nhanh hơn. Đại thể, lệnh *filter-branch* giúp bạn thay đổi cả một chương lớn của lịch sử chỉ chỉ bằng một lệnh đơn. Sau này, thư mục +.git/refs/original+ mô tả trạng thái của công việc trước khi thực hiện. Kiểm tra lệnh filter-branch đã làm những thứ bạn muốn chưa, sau đó xóa thư mục này đi nếu bạn muốn chạy lệnh filter-branch lần nữa. Cuối cùng, thay thế bản sao của dự án của bạn bằng phiên bản bạn đã sửa lại nếu bạn muốn tương thích với chúng sau này. === Tự Tạo Lịch Sử === [[makinghistory]] Bạn muốn chuyển đổi dự án của mình sang sử dụng Git? Nếu nó được quản lý bởi một hệ thống nổi tiếng hơn, thế thì nếu may mắn sẽ có người nào đó đã viết sẵn một đoạn kịch bản để xuất toàn bộ lịch sử ra thành Git. Nếu không, thì nên xem xét đến việc sử dụng lệnh *git fast-import*, lệnh này đọc văn bản đầu vào ở một định dạng riêng để mà tạo ra một lịch sử cho Git từ ban đầu. Thông thường một script sử dụng lệnh này là một nhóm lệnh tổ hợp với nhau và chỉ chạy một lần, di chuyển một dự án chỉ bằng một lệnh đơn. Dưới đây là một ví dụ, dán danh sách theo sau đâu vào một tập tin tạm thời nào đó, chẳng hạn như là `/tmp/history`: ---------------------------------- commit refs/heads/master committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000 data <<EOT Initial commit. EOT M 100644 inline hello.c data <<EOT #include <stdio.h> int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800 data <<EOT Replace printf() with write(). EOT M 100644 inline hello.c data <<EOT #include <unistd.h> int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- Thế thì tạo một kho Git từ thư mục chứa các tập tin tạm thời này bằng cách gõ: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history Bạn có thể checkout phiên bản cuối của dự án với: $ git checkout master . Lệnh *git fast-export* chuyển đổi bất kỳ một kho chứa nào thành định dạng phù hợp với lệnh *git fast-import*, và bạn có thể nghiên cứu nó để tạo riêng cho mình một chương trình xuất, và cũng làm như thế để tạo thành kho chuyên chở ở định dạng con người có thể đọc được. Thực vậy, những lệnh này có thể gửi một kho chứa ở dạng văn bản thông qua một kênh chỉ cho phép văn bản truyền đi. === Vị Trí Nào Phát Sinh Lỗi? === Bạn vừa mới phát hiện ra một đặc tính không hoạt động trong chương trình mà bạn chắc chắn là nó đã hoạt động vài tháng trước. Tệ quá! Bạn tự hỏi là lỗi bắt đầu từ chỗ nào nhỉ? Nếu như chỉ có mình bạn kiểm tra cũng như phát triển đặc tính này. Lúc này thì đã quá muộn rồi. Tuy nhiên, chỉ cần bạn commit thường xuyên, Git có thể xác định vị trí của trục trặc: $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git checks out một trạng thái nằm giữa chúng. Kiểm tra đặc tính kỹ thuật, và nếu nó vẫn hỏng: $ git bisect bad Nếu không thì thay "bad" bằng "good". Git sẽ chuyên chở bạn qua lại nửa bước giữa hai trạng thái là phiên bản "tốt" và "xấu", thu hẹp khả năng lại. Sau khi lặp đi lặp lại một số lần, việc tìm kiếm nhị phân này sẽ dẫn bạn đến lần commit mà nó làm nguyên nhân dẫn đễ hỏng hóc. Một khi bạn đã hoàn tất việc điều tra, trở lại trạng thái nguyên bản của bạn bằng cách gõ: $ git bisect reset Thay vì thử nghiệm mọi thay đổi một cách thủ công, hãy tự động hóa sự tìm kiếm này bằng cách chạy: $ git bisect run my_script Git sử dụng giá trị trả về của lệnh đã cho, thông thường là từ các đoạn kịch bản, để quyết định là lệnh đã thực hiện tốt hay không: lệnh sẽ trả về giá trị 0 khi tốt, 125 khi sự thay đổi bị bỏ qua, và bất kỳ giá trị nào khác nằm giữa 1 và 127 nếu gặp lỗi. Một giá trị âm sẽ bãi bỏ lệnh bisect. Bạn có thể làm nhiều hơn thế: trang trợ giúp giảng giải cho bạn làm thế nào để hiểu được lệnh bisect làm việc như thế nào, xem xét hay xem lại nhật ký lệnh bisect, và loại trừ các thay đổi ngớ ngẩn để tăng tốc độ tìm kiếm. === Ai Đã Làm Nó Sai? === Giống như các hệ thống quản lý mã nguồn khác, Git cũng có lệnh blame: $ git blame bug.c lệnh này chú thích tất cả các dòng có trong tập tin được chỉ ra cho thấy ai là người cuối cùng sửa nó, và là khi nào. Không giống các hệ thống quản lý mã nguồn khác, hành động này hoạt động không cần có mạng, việc đọc chỉ đơn thuần từ ổ đĩa của máy tính cá nhân. === Kinh Nghiệm Riêng === Trong một hệ thống quản lý mã nguồn tập trung, thay đổi lịch sử là một việc làm khó khăn, và chỉ làm được thế nếu đó là người quản trị. Việc nhân bản, tạo nhánh và trộn không thể thiếu việc truyền thông qua mạng. Cũng như thế với các tác vụ cơ bản khác như là duyệt lịch sử, hay là commit một thay đổi. Trong một số hệ thống khác, người dùng yêu cầu có kết nối mạng chỉ để xem các thay đổi của họ hay mở một tập tin để biên tập. Hệ thống tập trung không cho phép làm việc mà không có mạng, và đòi hỏi cơ sở hạ tầng mạng máy tính đắt đỏ tốn kém, đặc biệt là khi số nhà phát triển phần mềm tăng lên. Điều quan trọng, tất cả mọi tác vụ sẽ chậm hơn ở mức độ nào đó, thường thường những người sử dụng sẽ lảng tránh việc sử dụng các lệnh cao cấp trừ phi nó thực sự cần thiết. Trừ những trường hợp đặc biệt là các lệnh cơ bản. Khi những người dùng phải chạy các lệnh chạy chậm, hiệu suất bị giảm bởi vì nó làm gián đoạn công việc của cả một dây truyền. Tôi trực tiếp đã trải qua những hiện tượng đó. Git là hệ thống quản lý mã nguồn đầu tiên tôi sử dụng. Tôi nhanh chóng làm quen với nó, bị quyến rũ bởi những đặc tính kỹ thuật mà nó đã cung cấp. Tôi đơn giản cho rằng các hệ thống khác thì cũng tương tự: việc chọn lựa một hệ thống quản lý mã nguồn thì cũng chẳng khác việc chọn một trình biên soạn hay một trình duyệt web. Tôi sẽ sốc nếu như sau này bị bắt buộc sử dụng hệ thống quản lý mã nguồn tập trung. Một kết nối Internet chậm chạp cũng chẳng phải là vấn đề lớn đối với Git, nhưng nó sẽ làm cho các nhà phát triển phần mềm không thể chịu nổi khi nó cần sự tin cậy như ổ đĩa nội bộ. Thêm nữa, tôi đã gặp một số mắc mớ với một số lệnh, mà chính nó đã ngăn cản tôi làm việc một cách trôi chảy. Khi tôi phải chạy những lệnh cần nhiều thời gian, việc làm ngắt quãng việc suy nghĩ sẽ gây nên thiệt hại rất to lớn. Trong khi chờ cho việc truyền thông với máy chủ hoàn tất, tôi sẽ phải làm một vài thứ gì đó khác để lấp chỗ trống, chẳng hạn như lấy thư điện tử hay viết tài liệu. Sau một khoảng thời gian tôi quay trở lại nhiệm vụ chính của mình, lệnh đã hoàn tất từ lâu rồi, và tôi phải lãng phí thêm nhiều thời gian nữa để nhớ lại xem mình đang làm gì. Con người thường dở khi phải thay đổi mạch văn. Ở đây còn có một hậu quả rất đáng quan tâm nữa: đoán trước được việc tắc nghẽn của mạng máy tính, nhiều cá nhân riêng lẻ có thể chiếm dụng nhiều lưu lượng mạng hơn cần thiết trên các tác vụ khác nhau để cố gắng giảm thiểu sự chậm trễ có thể xảy ra trong tương lai. Hậu quả cuối cùng là sự quá tải quá mức, chính việc vô tình ủng hộ việc tiêu dùng cá nhân như thế đã tiêu tốn nhiều lưu lượng mạng hơn và sau đó nó làm cho việc tắc nghẽn càng lúc càng trở nên tồi tệ hơn. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/translate.txt������������������������������������������������������������������0000644�0001750�0001750�00000003416�12666307504�016160� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Phụ lục B: Dịch cuốn sách này == Tôi khuyến nghị các bạn làm theo các bước sau để thực hiện việc dịch thuật, làm như vậy thì các đoạn kịch bản của tôi có thể nhanh chóng tạo ra các tài liệu ở định dạng HTML và PDF, và tất cả các bản dịch có thể ở trong cùng một kho chứa. Lấy một bản sao của mã nguồn, sau đó tạo một thư mục tương ứng với ngôn ngữ bạn dịch http://www.iana.org/assignments/language-subtag-registry[language's IETF tag]: xem tại http://www.w3.org/International/articles/language-tags/Overview.en.php[the W3C article on internationalization]. Ví dụ, tiếng Anh là "en", Nhật là "ja", tiếng Trung Quốc Phồn thể là "zh-Hant". Trong thư mục mới đó, và dịch tập tin +txt+ từ thư mục con "en". Một ví dụ cụ thể là để dịch phần hướng dẫn này thành ngôn ngữ http://en.wikipedia.org/wiki/Klingon_language[Klingon], bạn hãy gõ vào: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" là mã ngôn ngữ IETF dành cho Klingon. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Dịch tập tin này. và cứ như thế cho những tập tin còn lại. Chỉnh sửa lại Makefile và thêm mã ngôn ngữ cho biến `TRANSLATIONS`. Bạn có thể xem thử kết quả công việc của mình như sau: $ make tlh $ firefox book-tlh/index.html Hãy chuyển giao công việc của bạn thường xuyên, và cho tôi biết khi nào thì chúng sẵn sàng để sử dụng. GitHub.com có giao diện thuận lợi như sau: rẽ nhánh dự án "gitmagic", 'push' các thay đổi của bạn lên, sau đó thông báo với tôi để tôi trộn. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/intro.txt����������������������������������������������������������������������0000644�0001750�0001750�00000023206�12666307504�015315� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Giới thiệu == Tôi sử dụng cách ví von để giới thiệu về hệ thống quản lý mã nguồn. Xem http://en.wikipedia.org/wiki/Revision_control[bài viết về quản lý mã nguồn trên Wikipedia] để có được sự giải thích thỏa đáng. === Công Việc giống như Trò Chơi === Tôi đã chơi điện tử trên máy tính suốt từ bé đến giờ. Nhưng tôi chỉ bắt đầu sử dụng hệ thống quản lý mã nguồn khi đã trưởng thành. Tôi tin rằng không chỉ có mình tôi như thế, và việc so sánh giữa hai điều đó sẽ làm cho các khái niệm trở nên dễ hiểu, dễ giải thích hơn. Hãy nghĩ việc biên soạn mã nguồn, tài liệu cũng giống như việc chúng ta đang chơi trò chơi điện tử trên máy tính. Một khi bạn đã làm được kha khá, bạn sẽ muốn ghi lại thành quả công việc của mình. Để làm điều đó, bạn chỉ việc bấm vào nút 'Save' trong chương trình biên soạn của mình. Nhưng việc làm này sẽ ghi đè lên dữ liệu của bản cũ. Điều này cũng giống như các trò chơi đã cũ chỉ cho phép ghi trên một tập tin: bạn phải chắc chắn là mình muốn ghi lại, nếu không thì bạn không bao giờ có thể quay lại trạng thái cũ nữa. Và thật không may vì lần lưu trước đó có thể là đúng tại một điểm rất hay trong lượt chơi và bạn muốn quay lại đó sau này. Tệ hơn nữa là khi bản ghi lại hiện tại lại không đúng, và thế là bạn sẽ phải bắt đầu lại từ đầu. === Quản Lý Mã Nguồn === Khi biên soạn, bạn có thể chọn 'Save As...' để ghi lại tập tin hiện tại nhưng với một cái tên khác, hay là sao chép tập tin ra một chỗ khác trước khi bạn ghi lại, nếu như bạn muốn dùng cả các bản cũ. Bạn có thể nén chúng lại để tiết kiệm dung lượng lưu trữ. Đây là dạng thức nguyên thủy và tốn nhiều công sức cho việc quản lý dữ liệu. Trò chơi trên máy tính đã cải tiến cách trên từ rất lâu rồi, rất nhiều trong số chúng cung cấp cho bạn khả năng tự động ghi lại sau từng khoảng thời gian nhất định. Chúng ta sẽ giải quyết một vấn đề hơi hóc búa một chút nhé! Bạn nói rằng bạn có nhiều tập tin có liên quan mật thiết với nhau, như mã nguồn cho một dự án chẳng hạn, hay các tập tin cho một website. Bây giờ nếu bạn muốn giữ một phiên bản cũ bạn phải lưu giữ toàn bộ thư mục. Giữ nhiều phiên bản như thế bằng cách thủ công thật bất tiện, và sẽ nhanh chóng trở nên tốn kém. Đối với một số trò chơi, ghi lại một trò chơi thực tế là bao gồm toàn bộ thư mục. Những trò chơi này thực thi việc này tự động và chỉ đưa ra một giao diện thích hợp cho người chơi để quản lý các phiên bản của thư mục này. Các hệ thống quản lý mã nguồn cũng hoạt động theo cách ấy. Chúng có một giao diện tinh tế để quản lý một nhóm các thứ trong thư mục. Bạn có thể ghi lại trạng thái của thư mục một cách thường xuyên, và bạn có thể tải lên bất kỳ một trạng thái nào đã được ghi lại trước đó. Không giống như các trò chơi trên máy tính, chúng thường khôn khéo hơn về việc tiết kiệm không gian lưu trữ. Thông thường, chỉ có một số ít tài liệu được sửa đổi giữa các phiên bản, và cũng không nhiều. Nó chỉ lưu giữ những cái có thay đổi thay vì toàn bộ tất cả. === Hệ Thống Phân Tán === Bây giờ hãy tưởng tượng có một trò chơi rất khó. Khó để hoàn thành đến mức là có nhiều game thủ lão luyện trên toàn thế giới quyết định lập thành đội và chia sẻ những trò chơi mà họ đã lưu lại với mục đích là để tất cả mọi người có thể theo dõi được nhau. *Speedruns* là những ví dụ trong đời sống thực: các đấu thủ được phân hóa theo các mức của cùng một trò chơi hợp tác với nhau để đạt được các kết quả đáng kinh ngạc. Làm thế nào bạn có thể cài đặt một hệ thống mà chúng có thể lấy được từng bản ghi của mỗi người một cách dễ dàng? Và tải lên cái mới hơn? Ngày xưa, mọi dự án đều sử dụng hệ thống quản lý tập trung. Máy chủ ở một chỗ đâu đó và giữ tất cả các trò chơi đã được ghi lại. Không còn ai khác làm điều đó nữa. Mọi người giữ phần lớn các trò chơi được ghi lại trong máy của họ. Khi một đấu thủ muốn chơi, họ có thể tải về bản ghi lại cuối cùng đã lưu lại ở máy chủ, chơi một lúc, ghi lại và tải trở lại máy chủ để những người khác có thể sử dụng. Điều gì xảy ra khi một người chơi, vì một lý do nào đó, muốn có được lần chơi cũ hơn? Lý do có thể là lần chơi hiện tại không ổn định hay có sai sót bởi vì một người chơi nào đó quên không chỉnh lại trò chơi về mức 3, và họ muốn tìm lần chơi đã ghi lại cuối cùng mà họ vẫn chưa hoàn thành. Hay có thể là họ muốn so sánh sự khác nhau giữa các lần chơi để thấy được thành quả của từng người chơi. Có rất nhiều lý do vì sao cần đến bản cũ hơn, nhưng kết cục là giống nhau. Họ phải hỏi máy chủ trung tâm để lấy về trò chơi cũ đã được lưu lại. Càng ghi lại nhiều trò chơi, họ càng cần phải liên lạc nhiều với nhau. Những hệ thống quản lý mã nguồn thế hệ mới, Git là một trong số đó, được biết đến như một hệ thống phân tán, và có thể coi nó là một hệ thống tập trung có mở rộng. Khi người chơi tải về từ máy chủ chính, họ lấy toàn bộ tất cả các lần đã ghi lại, không chỉ mỗi bản cuối cùng. Điều đó có nghĩa là họ trở thành bản sao của máy chủ trung tâm. Việc khởi tạo bản sao như thế có vẻ hơi xa hoa, đặc biệt là nếu nó có lịch sử phát triển lâu dài, nhưng cái giá phải trả cũng chỉ là việc cần nhiều thời gian để lấy về. Một lợi ích trực tiếp của việc này là khi các tài liệu cũ cần đến, việc liên lạc với máy chủ trung tâm là không cần thiết nữa. === Quan Niệm Cổ Hủ === Một quan niệm phổ biến là hệ thống phân tán không thích hợp với các dự án có yêu cầu một kho chứa trung tâm chính thức. Không điều gì có thể chà đạp lên sự thật. Chụp ảnh ai đó không có nghĩa là lấy đi linh hồn họ. Cũng như thế, nhân bản kho chính cũng không làm giảm đi sự quan trọng của nó. Tóm lại, một hệ thống phân tán đã thiết kế tốt thì làm bất cứ công việc nào cũng khá hơn một hệ thống quản lý mã nguồn tập trung. Tài nguyên mạng thường thì tốn kém hơn các tài nguyên nội bộ. Chúng ta sẽ nói đến các hạn chế của hệ thống phân tán sau, sự so sánh như sau thường đúng: hệ thống phân tán thường tốt hơn. Một dự án nhỏ có thể chỉ cần dùng một phần nhỏ các đặc tính được đưa ra bởi một hệ thống như thế, nhưng việc sử dụng một hệ thống không có khả năng mở rộng cho một dự án nhỏ thì cũng giống như việc sử dụng hệ thống số La Mã để tính toán các số nhỏ. Hơn thế nữa, dự án của bạn có thể lớn vượt ra ngoài dự kiến ban đầu. Việc sử dụng Git từ lúc khởi sự thì cũng giống như việc sử dụng một bộ dao vạn năng chỉ để phục vụ cho mỗi việc mở nút chai. Đến một ngày nào đó bạn cấn đến một cái chìa vít bạn sẽ vui sướng vì mình không chỉ có mỗi cái mở nút chai. === Xung Đột Khi Trộn === Với chủ đề này, dùng cách ví von nó với một trò chơi trên máy tính là hơi khó. Thay vì thế, để chúng tôi dùng việc biên soạn một tài liệu để giải thích cho bạn. Giả sử Alice chèn thêm một dòng vào đầu một tập tin, và Bob nối một dòng vào cuối của bản sao của mình. Cả hai đều tải lên các thay đổi của mình. Phần lớn các hệ thống sẽ tự động tìm ra hành động hợp lý: chấp nhận và trộn các sự thay đổi của họ, do đó cả hai sự thay đổi mà Alice và Bob tạo ra đều được dùng. Bây giờ giả sử cả Alice và Bob cùng sửa một dòng. Thế thì mâu thuẫn này không thể sử lý được mà không có sự can thiệp của con người. Người thứ hai tải lên sẽ được thông báo có xung đột xảy ra, _merge conflict_, và phải chọn một là sửa thêm nữa, hay sửa lại toàn bộ dòng đó. Nhiều tình huống phức tạp có thể nảy sinh. Hệ thống quản lý mã nguồn giữ phần dễ dàng cho chúng, và để lại những tình huống khó khăn cho chúng ta. Nhưng thông thường cách ứng xử của chúng có thể điều chỉnh được. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/vi/clone.txt����������������������������������������������������������������������0000644�0001750�0001750�00000033763�12666307504�015273� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Nhân Bản == Trong các hệ thống quản lý mã nguồn trước đây, checkout là tác vụ cơ bản để lấy các tập tin về. Bạn lấy về toàn bộ các tập tin được lưu giữ trong từng phiên bản riêng biệt. Với Git và các hệ thống quản lý mã nguồn phân tán, clone là tác vụ cơ bản. Để lấy các tập tin, bạn lấy về toàn bộ kho chứa. Nói cách khác, bạn thực tế là một bản sao của máy chủ trung tâm. Bất kỳ cái gì bạn thể làm được với kho chứa chính thì cũng làm được ở đây. === Đồng bộ hóa Các Máy tính === Tôi có thể tạo gói *tarball* hay sử dụng lệnh *rsync* để sao lưu dự phòng và đồng bộ hóa dữ liệu. Nhưng thỉnh thoảng, tôi biên tập trên máy tính xách tay của mình, nhưng lúc khác lại trên máy tính để bàn, và chúng có thể không có sự trao đổi được với nhau. Khởi tạo kho chứa Git và commit các tập tin trên một máy tính. Sau đó trên máy tính kia chạy lệnh: $ git clone other.computer:/path/to/files để tạo một bản sao thứ hai cho các tập tin và kho chứa. Từ giờ trở đi, $ git commit -a $ git pull other.computer:/path/to/files HEAD sẽ lấy về một trạng thái của các tập tin trên máy tính khác về máy bạn đang làm việc. Nếu bạn vừa tạo ra một sự chỉnh sửa xung đột trong cùng một tập tin, Git sẽ cho bạn biết và bạn có thể chuyển giao sau khi đã sửa chữa chúng. === Quản lý theo cách Cũ === Khởi tạo kho Git cho các tập tin của bạn: $ git init $ git add . $ git commit -m "Lần commit khởi tạo" Trên máy chủ trung tâm, khởi tạo kho 'thuần' ở một thư mục nào đó: $ mkdir proj.git $ cd proj.git $ git --bare init $ touch proj.git/git-daemon-export-ok Khởi động dịch vụ Git nếu cần: $ git daemon --detach # nó có thể đã hoạt động rồi Để làm một máy chủ chạy dịch vụ Git, làm theo các chỉ dẫn sau để cài đặt và khởi tạo kho Git. Cách thường thấy nhất là điền vào mẫu có sẵn trên trang web. 'Push' dự án của bạn lên máy chủ trung tâm bằng lệnh: $ git push central.server/path/to/proj.git HEAD Để lấy về mã nguồn, các nhà phát triển phần mềm chỉ cần gõ: $ git clone central.server/path/to/proj.git Sau khi thay đổi, các nhà phát triển phần mềm sẽ lưu lại các thay đổi trên máy tính của mình: $ git commit -a Để cập nhật lên bản mới nhất: $ git pull Mọi xung đột phải được xử lý trước, sau đó mới chuyển giao: $ git commit -a Gửi thay đổi của mình lên máy chủ trung tâm: $ git push Nếu máy chủ trung tâm có thay đổi bởi hành động của một người phát triển phần mềm khác, quá trình push sẽ bị lỗi, và anh ta phải pull về bản mới nhất, xử lý các xung đột khi trộn, sau đó thử lại. Người dùng phải có quyền truy cập SSH mới có thể thực hiện được lệnh pull và push ở trên. Tuy nhiên, ai cũng có thể lấy mã nguồn về bằng lệnh: $ git clone git://central.server/path/to/proj.git Giao thức git nguyên bản thì cũng giống như là HTTP: ở đây không cần xác thực, do vậy ai cũng có thể lấy về dự án. Do vậy, theo mặc định, việc push thông qua giao thức git là không được phép. === Mã nguồn riêng tư === Với một dự án nguồn-đóng, bỏ quên lệnh touch, và chắc chắn là chưa từng tạo ra file nào có tên `git-daemon-export-ok`. Kho chứa từ giờ trở đi không thể lấy về thông qua giao thức git; chỉ những người có khả năng truy cập bằng SSH mới có thể thấy nó. Nếu tất cả các kho chứa đều đóng, việc chạy git daemon là không cần thiết nữa bởi vì tất cả việc truyền thông bây giờ đều thông qua SSH. === Kho thuần === Kho thuần (bare) được đặt tên như vậy vì nó không chứa thư mục làm việc; nó 'thuần túy' chứa các tập tin thường là ẩn trong thư mục con `.git`. Hay nói cách khác, nó chứa lịch sử mã nguồn của một dự án, và không bao giờ giữ các tập tin bất kỳ phiên bản nào. Kho thuần có vai trò hoạt động giống như máy chủ trung tâm trong các hệ thống quản lý mã nguồn tập trung: thư mục chủ dự án của bạn. Các nhà phát triển phần mềm nhân bản dữ liệu dự án của bạn ở đây, và 'push' các thay đổi chính thức lên đó. Thông thường nó đặt tại máy chủ mà máy này đôi khi còn làm một số việc khác nữa. Sự biên soạn mã nguồn xảy ra trên bản sao, vì vậy thư mục chủ của kho chứa có thể hoạt động mà không cần thư mục làm việc. Nhiều lệnh Git gặp lỗi trên kho thuần trừ phi biến môi trường `GIT_DIR` được đặt với giá trị là đường dẫn đến kho chứa, hay tùy chọn `--bare` được áp dụng. === Push ngược với pull === Tại sao chúng tôi lại giới thiệu lệnh 'push', thay vì trông cậy vào lệnh 'pull' quen thuộc? Trước hết, việc pull gặp lỗi trên kho thuần: thay vào đó bạn phải dùng lệnh 'fetch', lệnh này chúng ta sẽ nói sau. Nhưng dù là chúng ta giữ kho chứa thông thường trên máy chủ trung tâm, việc 'pull' lên nó hơi cồng kềnh. Chúng ta phải đăng nhập vào máy chủ trước, và cung cấp cho lệnh 'pull' địa chỉ mạng của máy chúng ta đang 'pull' từ đó. Chương trình tường lửa có thể gây trở ngại, và điều gì xảy ra khi chúng ta không có khả năng truy cập 'hệ vỏ' máy chủ trong chỗ đầu tiên đó? Tuy nhiên, ngoài trường hợp này ra, chúng ta còn nản lòng với việc push lên kho chứa, bởi vì tình trạng hỗn loạn có thể xảy khi thư mục đích có chứa thư mục làm việc. Tóm lại, khi bạn học Git, chỉ 'push' khi đích là kho thuần; nếu không thì dùng 'pull'. === Rẽ nhánh một dự án === Bạn chán ngấy cách mà dự án mà bạn đang làm việc chạy? Bạn nghĩ mình có thể làm tốt hơn thế? Thế thì trên máy chủ của mình: $ git clone git://main.server/path/to/files Tiếp theo, nói với mọi người về việc nhánh rẽ từ dự án tại máy chủ của bạn. Từ bây giờ trở đi, bạn có thể trộn các sự thay đổi từ dự án gốc với lệnh: $ git pull === Sao lưu không giới hạn === Bạn muốn lưu trữ dự án của mình ở nhiều nơi khác nhau ư? Nếu dự án của bạn có nhiều người cùng phát triển, bạn không cần phải làm gì cả! Mỗi một bản sao đều đồng thời có tác dụng như một bản sao lưu dự phòng. Mỗi bản sao không chỉ lưu trạng thái hiện hành mà toàn bộ lịch sử trong dự án. Nhờ có giá trị băm bằng mật mã, nếu bản sao của người nào đó bị hỏng, nó sẽ được phát hiện ngay sau khi họ liên lạc với những người khác. Nếu dự án của bạn không phổ biến, hãy tìm máy chủ lưu giữ bản sao của mình càng nhiều càng tốt. Người mắc bệnh hoang tưởng sẽ luôn luôn ghi ra 20-byte giá trị băm SHA1 cuối cùng của HEAD ở đâu đó an toàn. Nó phải an toàn, không riêng tư. Ví dụ, xuất bản nó lên báo giấy cũng tốt, bởi vì rất khó để thay đổi tất cả các bản sao của nó. === Làm nhiều việc cùng lúc === Bạn muốn làm nhiều việc cùng một lúc trên một dự án. Thế thì hãy commit dự án của bạn và chạy: $ git clone . /some/new/directory Nhờ có http://en.wikipedia.org/wiki/Hard_link[liên kết cứng], việc nhân bản nội bộ yêu cầu ít không gian và thời gian hơn so với việc sao lưu thông thường. Bây giờ bạn có thể làm hai công việc độc lập nhau cùng một lúc. Ví dụ như, bạn có thể biên soạn trên bản sao này trong khi bản sao kia đang được biên dịch. Tại bất kỳ thời điểm nào, bạn cũng có thể commit và pull các thay đỏi từ một bản sao khác: $ git pull /the/other/clone HEAD === Song hành cùng các hệ thống SCM khác === Bạn đang làm việc cho một dự án có sử dụng hệ thống quản lý mã nguồn cũ, và bạn lại muốn sử dụng Git? Thế thì hãy khởi tạo kho chứa Git trong thư mục bạn đang làm việc: $ git init $ git add . $ git commit -m "Lần commit khởi tạo" sau đó nhân bản nó: $ git clone . /some/new/directory Bây giờ hãy chuyển đến thư mục mới đó và làm việc ở đây thay vì chỗ cũ, sử dụng Git để thỏa mãn tình yêu của mình. Đôi khi, bạn sẽ muốn đồng bộ hóa với những người khác nữa, trong trường hợp đó hãy chuyển tới thư mục nguyên gốc, đồng bộ hóa bằng cách sử dụng một hệ thống quản lý mã nguồn khác, và gõ: $ git add . $ git commit -m "Đồng bộ hóa với những người khác" Sau đó chuyển tới thư mục mới và chạy: $ git commit -a -m "Mô tả về các thay đổi của mình" $ git pull Thủ tục đem lại các thay đổi của bạn tới những người khác nữa trên hệ thống quản lý mã nguồn khác. Thư mục mới có chứa các tập tin mà bạn thay đổi. Chạy các lệnh mà hệ thống quản lý mã nguồn khác cần để tải chúng lên kho chứa trung tâm. Subversion, có lẽ là hệ thống quản lý mã nguồn tập trung tốt nhất, được sử dụng bởi vô số các dự án. Lệnh *git svn* sẽ tự động hóa những việc đã nói ở trên dành cho Subversion, bạn cũng có thể làm như thế để http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[xuất dự án Git thành Subversion]. === Mercurial === Mercurial là hệ thống tương tự như hệ thống quản lý mã nguồn có thể làm việc gần giống như Git. Với plugin `hg-git`, người sử dụng Mercurial có thể push và pull từ kho Git mà không mất mát gì. Lấy plugin `hg-git` dành cho Git bằng cách: $ git clone git://github.com/schacon/hg-git.git hay là sử dụng Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ Thật buồn là tôi không biết rằng có một plugin tương tự dành cho Git. Vì vậy, tôi ủng hộ Git hơn Mercurial khi dùng cho kho chứa chính, dù là bạn thích Mercurial hơn. Với một dự án Mercurial, thường thường một người tình nguyện duy trì kho Git song song cho tương thích với người sử dụng Git, cũng phải cảm ơn plugin `hg-git`, một dự án Git tự động tiếp nhận người sử dụng Mercurial. Mặc dù plugin có thể chuyển đổi Mercurial sang Git bằng cách push tới một kho rỗng, công việc này là dễ dàng với script `hg-fast-export.sh`, đã sẵn dùng từ: $ git clone git://repo.or.cz/fast-export.git Để chuyển đổi, trong một thư mục rỗng hãy chạy: $ git init $ hg-fast-export.sh -r /hg/repo sau khi đặt script vào trong biến môi trường `$PATH`. === Bazaar === Chúng tôi đề cập vắn tắt về Bazaar bởi vì nó là hệ thống quản lý mã nguồn phân tán miễn phí và phổ biến nhất chỉ sau Git và Mercurial. Bazaar có lợi thế vì phát triển sau, có tuổi tương đối trẻ; những người thiết kế ra nó có thể học hỏi được nhiều từ các sai lầm trong quá khứ, và tránh được vết xe đổ. Ngoài ra, các nhà phát triển còn lưu tâm đến khả năng chuyển đổi và tương tác với các hệ thống quản lý mã nguồn khác. Plugin `bzr-git` giúp người dùng Bazaar làm việc với kho Git trong chừng mực nào đó. Chương trình chuyển đổi Bazaar thành Git, và có thể làm hơn thế nữa, trong khi `bzr-fast-export` thích hợp nhất cho việc chuyển đổi một lần duy nhất. === Tại sao Tôi dùng Git? === Trước tiên, tôi chọn Git bởi tôi nghe nói nó làm được việc phi thường là có thể quản lý mã nguồn cho một thứ khó quản lý như hạt nhân của hệ điều hành *Linux*. Tôi chưa bao giờ nghĩ đến việc chuyển sang dùng một phần mềm quản lý mã nguồn khác. Git làm được những việc thật đáng ngưỡng mộ, và tôi chưa từng bao giờ gặp các vấn đề với sai sót của nó. Do tôi hoạt động chủ yếu trên Linux, phát hành trên các nền tảng khác không phải là điều mà tôi quan tâm. Ngoài ra, tôi thích lập trình bằng ngôn ngữ C và bash scripts để thực thi như là ngôn ngữ kịch bản Python: ở đây có rất ít sự phụ thuộc, và tôi đam mê với những hệ thống thi hành nhanh chóng. Tôi đã nghĩ về việc làm thế nào để Git có thể phát triển, xa hơn nữa là tự mình viết một công cụ tương tự như Git, nhưng đây không phải là bài tập có tính thực tế. Khi tôi hoàn thành dự án của mình, dù sao đi nữa tôi vẫn sẽ dùng Git, với lợi thế là có thể chuyển đổi từ hệ thống cũ sang một cách nhanh chóng. Theo lẽ tự nhiên, những thứ cần thiết cho bạn và những thứ bạn mong muốn có lẽ khác nhau, và bạn có thể tốt hơn nếu mình làm việc với hệ thống khác. Dù sao đi nữa, bạn sẽ không bao giờ phải hối tiếc vì đã chọn Git. �������������gitmagic-20160304/pl/�������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12666307504�013413� 5����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pl/drawbacks.txt������������������������������������������������������������������0000644�0001750�0001750�00000020500�12666307504�016112� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Załącznik A: Niedociągnięcia Gita == O kilku problemach mogących wystąpić z Gitem nie wspomniałem do tej pory. Niektóre z nich można łatwo rozwiązać korzystając ze skryptów i 'hooks', inne wymagają reorganizacji i ponownego zdefiniowania całego projektu, a na rozwiązanie kilku innych uniedogodnień możesz tylko uzbroić się w cierpliwość i czekać na ich usunięcie. Albo jeszcze lepiej, samemu się nimi zająć i spróbować pomóc. === Słabości SHA1 === Z biegiem czasu kryptografowie odkrywają coraz więcej słabości systemu SHA1. Już dzisiaj byłoby możliwe dla przedsięwzięć dysponujących odpowiednimi zasobami finansowymi znaleźć kolizje w hashach. Za kilka lat, całkiem możliwe, że normalny domowy PC będzie dysponował odpowiednim zasobem mocy obliczeniowej, by skorumpować niepostrzeżenie repozytorium Gita. Miejmy nadzieję, że Git przestawi się na lepszą funkcje hashującą, zanim badania nad SHA1 zrobią go bezużytecznym. === Microsoft Windows === Korzystanie z Gita pod Microsoft Windows może być frustrujące: - http://cygwin.com/[Cygwin], unixoidalne środowisko dla Windowsa posiada http://cygwin.com/packages/git/[port Gita]. - http://code.google.com/p/msysgit/[Git dla MSys] jest jedną z alternatyw, niektóre polecenia wymagają jednak poprawek. === Pliki niepowiązane === Jeśli twój projekt jest bardzo duży i zawiera wiele plików, które nie są bezpośrednio ze sobą związane, mimo to jednak często zostają zmieniane, Git może tu działać gorzej niż inne systemy, ponieważ nie prowadzi monitoringu poszczególnych plików. Git kontroluje zawsze całość projektu, co w normalnym wypadku jest zaletą. Jednym z możliwych rozwiązań mogłoby być podzielenie twojego projektu na kilka mniejszych, w których znajdują się jedynie pliki od siebie zależne. Korzystaj z *git submodule* jeśli mimo to chcesz cały twój projekt mieć w tym samym repozytorium. === Kto nad czym pracuje? === Niektóre systemy kontroli wersji zmuszają cię, by w jakiś sposób oznaczyć pliki nad którymi pracujesz. Mimo że jest to bardzo uciążliwe, gdyż wymaga ciągłej komunikacji z serwerem centralnym, posiada to też swoje zalety: 1. Różnice zostają szybko znalezione, ponieważ wystarczy skontrolować wyłącznie oznaczone pliki. 2. Każdy może szybko sprawdzić, kto aktualnie nad czym pracuje, sprawdzając na serwerze po prostu kto zaznaczył jakie dane do edycji. Używając odpowiednich skryptów uda ci się to również przy pomocy Gita. Wymaga to jednak współdziałania programistów, ponieważ muszą również korzystać z tych skryptów podczas pracy nad projektem. === Historia pliku === Ponieważ Git loguje zmiany tylko dla całości projektu jako takiego, rekonstrukcja przebiegu zmian pojedynczego pliku jest bardziej pracochłonna, niż w innych systemach kontrolujących pojedyncze . Te wady są w większości przypadków uznawane za marginalne i nie są brane pod uwagę, ponieważ inne operacje są bardzo wydajne. Na przykład polecenie `git checkout` jest szybsze niż `cp -a`, zmiany w zakresie całego projektu daje się lepiej komprymować niż zbiór zmian na bazie pojedynczych plików. === Pierwszy klon === Wykonanie klonu jest kosztowniejsze niż w innych systemach kontroli wersji jeśli istnieje długa historia. Początkowy koszt spłaca się jednak na dłuższą metę ponieważ większość przyszłych operacji przeprowadzane będzie szybko i offline. Niemniej jednak istnieją sytuacje, w których lepiej utworzyć powierzchowny klon korzystając z opcji `--depth`. Trwa to o wiele krócej, taki klon jednak posiada też tylko ograniczoną funkcjonalność. === Niestałe projekty === Git został napisany z myślą optymalizacji prędkości działania przy dokonywaniu wielkich zmian. Ludzie robią jednak pomniejsze zmiany z wersji na wersję. Jakaś poprawka tutaj, jakaś nowa funkcja gdzie indziej, poprawienie komentarzy, itd. Ale jeśli twoje dane znacznie się od siebie różnią pomiędzy następującymi po sobie wersjami, to chcąc nie chcąc przy każdym 'commit' projekt zwiększy się o te zmiany. Nie wymyślono jednak do tej pory niczego w żadnym systemie kontroli wersji, by móc temu zapobiec, tutaj jednak użytkownik Gita cierpi najbardziej, ponieważ w normalnym wypadku klonuje cały przebieg projektu. Powinno się w takim wypadku szukać powodów wystąpienia największych zmian. Ewentualnie można czasami zmienić format danych. Małe zmiany w projekcie powinny pociągać tylko minimalne zmiany na tak wąskiej grupie plików, jak to tylko możliwe. Może czasami bardziej wskazana byłaby baza danych, czy jakiś system archiwizacji zamiast systemu kontroli wersji. Na przykład nie jest dobrym sposobem zastosowanie systemu kontroli wersji do zarządzania zdjęciami wykonywanymi periodycznie przez kamerę internetową. Jeśli dane ulegają ciągłym zmianom i naprawdę muszą być objęte kontrolą wersji, jedną z możliwości jest zastosowanie Gita w scentralizowanej formie. Każdy może dokonywać pobieżnych klonów, które mało co lub wcale nie mają nic do czynienia z przebiegiem projektu. Oczywiście w takim wypadku wiele funkcji Gita nie będzie dostępnych a zmiany muszą być przekazywane w formie 'patch'. Prawdopodobnie będzie to dość dobrze działać, mimo iż nie jest do końca jasne komu potrzebna jest znajomość przebiegu tak ogromnej ilości niestabilnych danych. Innym przykładem może być projekt, który zależny jest od firmware przyjmującej kształt wielkiej danej w formie binarnej. Historia pliku firmware nie interesuje użytkownika, a zmiany nie pozwalają się wygodnie komprymować, wielkość repozytorium wzrasta niepotrzebnie o nowe wersje binarnego pliku firmware. W takim wypadku należałoby trzymać w repozytorium wyłącznie kod źródłowy, a sam plik binarny poza nim. By ułatwić sobie życie, ktoś mógłby opracować skrypt, który Git wykorzystuje do klonowania kodu źródłowego i 'rsync' albo pobieżny klon dla samego firmware. === Licznik globalny === Wiele systemów kontroli wersji udostępnia licznik, który jest zwiększany z każdym "commit". Git natomiast odwołuje się przy zmianach do sum kontrolnych SHA1, co w wielu przypadkach jest lepszym rozwiązaniem. Niektórzy jednak przyzwyczaili się do tego licznika. Na szczęście, łatwo jest napisać skrypt zwiększający stan licznika przy każdej aktualizacji centralnego repozytorium Gita. Może w formie taga, który powiązany jest z sumą kontrolną ostatniego 'commit'. Każdy klon mógłby posiadać taki licznik, jednak byłby on prawdopodobnie bezużyteczny, ponieważ tylko licznik centralnego repozytoriom ma znaczenie. === Puste katalogi === Nie ma możliwości kontroli wersji pustych katalogów. Aby obejść ten problem wystarczy utworzyć w takim katalogu plik dummy. To raczej obecna implementacja Gita, a mniej jego konstrukcja, jest odpowiedzialna za to wadę. Przy odrobinie szczęścia, jeśli Git jeszcze bardziej się upowszechni i więcej użytkowników żądać będzie tej funkcji, to być może zostanie zaimplementowana. === Pierwszy 'commit' === Stereotypowy informatyk liczy od 0 zamiast 1. Niestety, w kwestii 'commits' Git nie podąża za tą konwencją. Wiele komend marudzi przed wykonaniem pierwszego 'commit'. Dodatkowo, różnego rodzaju krańcowe przypadki muszą być traktowane specjalnie, jak 'rebase' dla 'branch' o różniącym się pierwszym 'commit'. Git zyskałby na zdefiniowaniu tzw. 'zero-commit', ponieważ zaraz po zainicjowaniu repozytorium, 'HEAD' otrzymałby 20 bajtowy hash SHA1. Ten specjalny 'commit' reprezentowałby puste drzewo, bez rodziców, być może pradziad wszystkich repozytoriów. Jeśli na przykład użytkownik wykonałby polecenie *git log*, zostałby poinformowany, że nie istnieje jeszcze żaden 'commit', gdzie na dzień dzisiejszy taka komenda wywoła błąd. Analogicznie dzieje się też z innymi poleceniami. Każdy inicjujący 'commit' byłby pochodną tego zerowego 'commit'. Niestety występuje jeszcze kilka innych problemów. Jeśli chcemy scalić kilka 'branches' o różniących sie inicjalnych 'commits' i przeprowadzić 'rebase', musimy ingerować ręcznie. === Charakterystyka zastosowania === Dla 'commits' A i B, znaczenie wyrażeń "A..B" i "A...B" zależy od tego, czy polecenie oczekuje dwóch punktów końcowych, czy zakresu. Sprawdź *git help diff* i *git help rev-parse*. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pl/branch.txt���������������������������������������������������������������������0000644�0001750�0001750�00000031714�12666307504�015417� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Magia 'branch' == Szybkie, natychmiastowe działanie poleceń 'branch' i 'merge', to jedne z najbardziej zabójczych właściwości Gita. *Problem*: Zewnętrzne faktory narzucają konieczność zmiany kontekstu. Poważne błędy w opublikowanej wersji ujawniły się bez ostrzeżenia. Skrócono termin opublikowania pewnej właściwości. Autor, którego pomocy potrzebujesz w jednej z kluczowych sekcji postanawia opóścić projekt. We wszystkich tych przypadkach musisz natychmiastowo zaprzestać bieżących prac i skoncentrować się nad zupełnie innymi zadaniami. Przerwanie toku myślenia nie jest dobre dla produktywności, a czym większa różnica w kontekście, tym większe straty. Używając centralnych systemów kontroli wersji musielibyśmy najpierw zładować świeżą kopię roboczą z serwera. W systemach rozproszonych wygląda to dużo lepiej, ponieważ możemy żądaną wersje sklonować lokalnie Jednak klonowanie również niesie za sobą kopiowanie całego katalogu roboczego jak i całej historii projektu aż do żądanego punktu. Nawet jeśli Git redukuje wielkość przez podział danych i użycie twardych dowiązań, wszystkie pliki projektu muszą zostać odtworzone w nowym katalogu roboczym. *Rozwiązanie*: Git posiada lepsze narzędzia dla takich sytuacji, jest ono wiele szybsze i zajmujące mniej miejsca na dysku jak klonowanie: *git branch*. Tym magicznym słowem zmienisz dane w swoim katalogu roboczym z jednej wersji w inną. Ta przemiana potrafi dużo więcej jak tylko poruszać się w historii projektu. Twoje pliki mogą przekształcić się z aktualnej wersji do wersji eksperymentalnej, do wersji testowej, do wersji twojego kolegi i tak dalej. === Przycisk 'szef' === Być może grałaś już kiedyś w grę, która posiadała magiczny (``przycisk szef''), po naciśnięciu którego twój monitor natychmiast pokazywał jakieś arkusze kalkulacyjne, czy coś w tym roduaju? Celem przycisku było szybkie ukrycie gierki na wypadek pojawienia się szefa w twoim biurze. W jakimś katalogu: $ echo "Jestem mądrzejsza od szefa" > mój_plik.txt $ git init $ git add . $ git commit -m "Pierwszy commit" Utworzyliśmy repozytorium Gita, które zawiera plik o powyższej zawartości. Następnie wpisujemy: $ git checkout -b szef # wydaje się, jakby nic się nie stało $ echo "Mój szef jest ode mnie mądrzejszy" > mój_plik.txt $ git commit -a -m "Druga wersja" Wygląda jakbyśmy zmienili zawartość pliku i wykonali 'commit'. Ale to tylko iluzja. Wpisz: $ git checkout master # przejdź do oryginalnej wersji i hokus-pokus! Poprzedni plik jest przywrócony do stanu pierwotnego. Gdyby jednak szef zdecydował się grzebać w twoim katalogu, wpisz: $ git checkout szef # przejdź do wersji, która nadaje się do obejrzenia przez szefa Możesz zmieniać pomiędzy tymi wersjami pliku tak często jak zechcesz, każdą z tych wersji pliku możesz też niezależnie edytować. === Brudna robota === [[branch]] Załóżmy, że pracujesz nad jakąś funkcją i musisz z jakiegokolwiek powodu wrócić o 3 wersje wstecz w celu wprowadzenia kilku poleceń print, aby sprawdzić jej działanie. Wtedy: $ git commit -a $ git checkout HEAD~3 Teraz możesz na dziko wprowadzać tymczasowy kod. Możesz te zmiany nawet dodać do'commit'. Po skończeniu, $ git checkout master wróci cię do poprzedniej pracy. Zauważ, że wszystkie zmiany, które nie zostały zatwierdzone przez 'commit', zostały przejęte. A co jeśli chciałaś zapamiętać wprowadzone zmiany? Proste: $ git checkout -b brudy i tylko jeszcze wykonaj 'commit' zanim wrócisz do 'master branch'. Jeśli tylko chcesz wrócić do twojej brudnej roboty, wpisz po prostu $ git checkout brudy Spotkaliśmy się z tym poleceniem już we wcześniejszym rozdziale, gdy poruszaliśmy temat ładowania starych wersji. Teraz możemy opowiedzieć cala prawdę: pliki zmieniają się do żądanej wersji, jednak musimy opuścić 'master branch'. Każdy 'commit' od teraz prowadzi twoje dane inną drogą, której możemy również nadać nazwę. Innymi słowami, po przywołaniu ('checkout') starszego stanu Git automatycznie przenosi cię do nowego, nienazwanego 'branch', który poleceniem *git checkout -b* otrzyma nazwę i zostanie zapamiętany. === Szybka korekta błędów === Będąc w środku jakiejś pracy, otrzymujesz polecenie zajęcia się nowo znalezionym błędem w 'commit' `1b6d...`: $ git commit -a $ git checkout -b fixes 1b6d Po skorygowaniu błędu: $ git commit -a -m "Błąd usunięty" $ git checkout master i kontynuujesz przerwaną pracę. Możesz nawet ostatnio świeżo upieczoną poprawkę przejąć do aktualnej wersji: $ git merge fixes === Merge === Za pomocą niektórych systemów kontroli wersji utworzenie nowego 'branch' może i jest proste, jednak późniejsze połączenie ('merge') skomplikowane. Z Gitem 'merge' jest tak trywialne, że możesz czasem nawet nie zostać powiadomiony o jego wykonaniu. W gruncie rzeczy spotkaliśmy się już dużo wcześniej z funkcją 'merge'. Polecenie *git pull* ściąga inne wersje i łączy ('merge') z twoim aktualnym 'branch'. Jeśli nie wprowadziłaś żadnych lokalnych zmian, to 'merge' jest szybkim przejściem do przodu, jest to przypadek podobny do zładowania ostatniej wersji przy centralnych systemach kontroli wersji. Jeśli jednak wprowadziłaś zmiany, Git automatycznie wykona 'merge' i powiadomi cię o ewentualnych konfliktach. Zwyczajnie każdy 'commit' posiada matczyny 'commit', a mianowicie poprzedzający go 'commit'. Zespolenie kilku 'branch' wytwarza 'commit' z minimum 2 matczynymi 'commit'. To nasuwa pytanie, który właściwie 'commit' wskazuje na `HEAD~10`? Każdy 'commit' może posiadać więcej rodziców, za którym właściwie podążamy? Wychodzi na to, ze ta notacja zawsze wybiera pierwszego rodzica. To jest wskazane, ponieważ aktualny 'branch' staje się pierwszym rodzicem dla 'merge', częściej będziesz zainteresowany bardziej zmianami których dokonałaś w aktualnym 'branch', niż w innych. Możesz też wybranego rodzica wskazać używając symbol dzióbka. By na przykład pokazać logi drugiego rodzica. $ git log HEAD^2 Możesz pominąć numer pierwszego rodzica. By na przykład pokazać różnice z pierwszym rodzicem: $ git diff HEAD^ Możesz ta notacje kombinować także z innymi rodzajami. Na przykład: $ git checkout 1b6d^^2~10 -b archaiczne tworzy nowy 'branch' o nazwie 'archaiczne', reprezentujący stan 10 'commit' do tyłu drugiego rodzica dla pierwszego rodzica 'commit', którego hash rozpoczyna się na 1b6d. === Praca bez przestojów === W procesie produkcji często drugi krok planu musi czekać na zakończenie pierwszego. Popsuty samochód stoi w garażu nieużywany do czasu dostarczenia części zamiennej. Prototyp musi czekać na wyprodukowanie jakiegoś chipa zanim będzie można podjąć dalszą konstrukcje. W projektach software może to wyglądać podobnie. Druga część jakiegoś feature musi czekać, aż pierwsza zostanie wydana i przetestowana. Niektóre projekty wymagają sprawdzenia twojego kodu zanim zostanie zaakceptowany, musisz wiec czekać z następną częścią aż pierwsza zostanie sprawdzona. Dzięki bezbolesnemu 'branch' i 'merge' możemy te reguły naciągnąć i pracować nad druga częścią jeszcze zanim pierwsza zostanie oficjalnie zatwierdzona. Przyjmijmy, że wykonałaś 'commit' pierwszej części i przekazałaś do sprawdzenia. Przyjmijmy też, że znajdujesz się w 'master branch'. Najpierw przejdź do 'branch' o nazwie 'część2': $ git checkout -b część2 Pracujesz w części 2 i regularnie wykonujesz 'commit'. Błądzenie jest ludzkie i może się zdarzyć, że zechcesz wrócić do części 1 i wprowadzić jakieś poprawki. Jeśli masz szczęście albo jesteś bardzo dobry, możesz ominąć następne linijki. $ git checkout master # przejdź do części 1 $ fix_problem $ git commit -a # zapisz rozwiązanie $ git checkout część2 # przejdź do części 2 $ git merge master # połącz zmiany Ewentualnie, część pierwsza zostaje dopuszczona: $ git checkout master # przejdź do części 1 $ submit files # opublikuj twoja wersję $ git merge część2 # Połącz z częścią 2 $ git branch -d część2 # usuń branch część2 Znajdujesz się teraz z powrotem w 'master branch' posiadając 'część2' w katalogu roboczym. Dość łatwo zastosować ten sam trik na dowolną ilość części. Równie łatwo można utworzyć 'branch' wstecznie: przypuśćmy, właśnie spostrzegłaś, iż już właściwie jakieś 7 'commit' wcześniej powinnaś stworzyć 'branch'. Wpisz wtedy: $ git branch -m master część2 # Zmień nazwę "master" na "część2". $ git branch master HEAD~7 # utwórz ponownie "master" 7 'commits' do tyłu. Teraz 'master branch' zawiera cześć 1 a branch `część2` zawiera całą resztę. Znajdujemy się teraz w tym ostatnim 'branch'; utworzyliśmy `master` bez wchodzenia do niego, gdyż zamierzamy dalszą pracę prowadzić w 'branch' `część2`. Nie jest to zbyt często stosowane. Do tej pory przechodziliśmy do nowego 'branch' zaraz po jego utworzeniu, tak jak w: $ git checkout HEAD~7 -b master # Utwórz branch i wejdź do niego. === Reorganizacja składanki === Może lubisz odpracowywać wszystkie aspekty projektu w jednym 'branch'. Chcesz wszystkie bieżące zmiany zachować dla siebie, a wszyscy inni powinni zobaczyć twoje 'commit' po ich starannym zorganizowaniu. Wystartuj parę 'branch': $ git branch czyste # Utwórz branch dla oczyszczonych 'commits'. $ git checkout -b zbieranina # utwórz 'branch' i przejdź do niego w celu dalszej pracy. Następnie wykonaj zamierzone prace: pousuwaj błędy, dodaj nowe funkcje, utwóż kod tymczasowy i tak dalej, regularnie wykonując 'commit'. Wtedy: $ git checkout czyste $ git cherry-pick zbieranina^^ zastosuje najstarszy matczyny 'commit' z 'branch' ``zbieranina'' na 'branch' ``czyste''. Poprzez 'przebranie wisienek' możesz tak skonstruować 'branch', który posiada jedynie końcowy kod i zależne od niego pogrupowane 'commit'. === Zarządzanie 'branch' === Listę wszystkich 'branch' otrzymasz poprzez: $ git branch Standardowo zaczynasz w 'branch' zwanym ``master''. Wielu opowiada się za pozostawieniem ``master'' branch w stanie dziewiczym i tworzeniu nowych dla twoich prac. Opcje *-d* und *-m* pozwalają na usuwanie i przesuwanie (zmianę nazwy) 'branch'. Zobacz: *git help branch*. Nazwa ``master'' jest bardzo użytecznym stworem. Inni mogą wychodzić z założenia, że twoje repozytorium takowy posiada i że zawiera on oficjalną wersję projektu. Nawet jeśli mogłabyś skasować lub zmienić nazwę na inną powinnaś respektować tę konwencję. === Tymczasowe 'branch' === Po jakimś czasie zapewne zauważysz, że często tworzysz 'branch' o krótkiej żywotności, w większości z tego samego powodu: każdy nowy 'branch' służy jedynie do tego, by zabezpieczyć aktualny stan, aby móc wrócić do jednego z poprzednich punktów i poprawić jakieś priorytetowe błędy czy cokolwiek innego. Można to porównać do chwilowego przełączenia kanału telewizyjnego, by sprawdzić co dzieje się na innym. Lecz zamiast naciskać guziki pilota, korzystasz z poleceń 'create', 'checkout', 'merge' i 'delete'. Na szczęście Git posiada na te operacje skrót, który jest tak samo komfortowy jak pilot telewizora: $ git stash Polecenie to zabezpiecza aktualny stan w tymczasowym miejscu ('stash' = ukryj) i przywraca poprzedni stan. Twój katalog roboczy wygląda dokładnie tak, jak wyglądał zanim zacząłaś edycję. Teraz możesz poprawiać błędy, zładować zmiany z centralnego repozytorium ('pull') i tak dalej. Jeśli chcesz powrócić z powrotem do ukrytego ('stashed') stanu, wpisz: $ git stash apply # Prawdopodobnie będziesz musiał rozwiązać konflikty. Możesz posiadać więcej 'stash'-ów i traktować je w zupełnie inny sposób. Zobacz *git help stash*. Jak już prawdopodobnie się domyślasz, Git korzysta przy wykonywaniu tej magicznej sztuczki z funkcji 'branch' w tle. === Pracuj jak chcesz === Może pytasz się, czy 'branch' są warte tego zachodu. Jakby nie było, polecenia 'clone' są prawie tak samo szybkie i możesz po prostu poleceniem *cd* zmieniać pomiędzy nimi, bez stosowania ezoterycznych poleceń Gita. Przyjrzyjmy się takiej przeglądarce internetowej. Dlaczego pozwala używać tabów tak samo jak i nowych okien? Ponieważ udostępnienie obu możliwości pozwala na stosowanie wielu stylów. Niektórzy użytkownicy preferują otwarcie jednego okna przeglądarki i korzystają z tabów dla wyświetlenia różnych stron. Inni upierają się przy stosowaniu pojedynczych okien dla każdej strony, zupełnie bez korzystania z tabów. Jeszcze inni znowu wolą coś pomiędzy. Stosowanie 'branch' to jak korzystanie z tabów dla twojego katalogu roboczego, a klonowanie porównać można do otwarcia wielu okien przeglądarki. Obie operacje są szybkie i lokalne, dlaczego nie poeksperymentować i nie znaleźć dla siebie najbardziej odpowiedniej kombinacji. Git pozwoli ci pracować dokładnie tak jak chcesz. ����������������������������������������������������gitmagic-20160304/pl/secrets.txt��������������������������������������������������������������������0000644�0001750�0001750�00000033346�12666307504�015635� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Uchylenie tajemnicy == Rzućmy spojrzenie pod maskę silnika i wytłumaczymy w jaki sposób Git realizuje swoje cuda. Nie będę wchodził w szczegóły. Dla pogłębienia tematu odsyłam na http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[anielskojęzychny podręcznik użytkownika]. === Niewidzialność === Jak to możliwe, że Git jest taki niepostrzeżony? Zapominając na chwilę o sporadycznych 'commits' i 'merges', możesz pracować w sposób, jakby kontrola wersji w ogóle nie istniała. Chciałem powiedzieć, do czasu aż będzie ci potrzebna. A oto chodzi, byś był zadowolony z tego, że Git cały czas czuwa nad twoją pracą. Inne systemy kontroli wersji ciągle zmuszają cię do ciągłego borykania się z zagadnieniem samej kontroli i związanej z tym biurokracji. Pliki mogą być zabezpieczone przed zapisem, aż do momentu gdy uda ci się poinformować centralny serwer o tym, że chciałabyś nad nimi popracować. Przy wzroście liczby użytkowników nawet najprostsze polecenia stają się wolne jak ślimak. Gdy tylko zniknie sieć lub centralny serwer praca staje. W przeciwieństwie do tego, Git posiada kronikę całej swojej historii w podkatalogu .git twojego katalogu roboczego. Jest to twoja własna kopie całej historii, z którą mogłabyś pracować offline, aż do momentu gdy zechcesz wymienić dane z innymi. Posiadasz absolutną kontrolę nad losem twoich danych, ponieważ Git potrafi dla ciebie w każdej chwili odtworzyć zapamiętany poprzednio stan z właśnie podkatalogu .git. === Integralność === Z kryptografią przez większość ludzi łączona jest poufność informacji, jednak równie ważnym jej celem jest zabezpieczenie danych. Właściwe zastosowanie kryptograficznych funkcji hashujących (funkcji skrótu) może uchronić przed nieumyślnym lub celowym zniszczeniem danych. Klucz hashujący SHA1 mogłabyś wyobrazić sobie jako składający się ze 160 bitów numer identyfikacyjny jednoznacznie opisujący dowolny łańcuch znaków, i który spotkasz w sowim życiu jeden jedyny raz. Nawet i więcej niż to: wszystkie łańcuchy znaków, jakie ludzkość przez wiele generacji stworzyła. Sama suma konreolna SHA1 też jest łańcuchem znaków w formie bajtów. Możemy generować hashe SHA1 z łańcuchów samych zawierających inne hashe SHA1. Ta prosta obserwacja okazała się niesamowicie pożyteczna: jeśli cię to zainteresowało poszukaj informacji na temat 'hash chains'. Zobaczymy później w jaki sposób wykorzystuje je Git dla zapewnienia produktywności i integralności danych. Krótko mówiąc, Git przechowuje twoje dane w podkatalogu `.git/objects`, gdzie zamiast nazw plików znajdziesz numery identyfikacyjne. Poprzez wykorzystanie tych numerów identyfikacyjnych jako nazwy plików razem z kilkoma innymi trikami związanymi z plikami blokującymi i znacznikami czasu, Git zamienia twój prosty system plików na produktywną i solidną bazę danych. === Inteligencja === Skąd Git wie o tym, że zmieniłaś nazwę jakiegoś pliku, jeśli nigdy go o tym wyraźnie nie poinformowałaś? Oczywiście, być może użyłaś polecenia *git mv*, jest to jednak to samo jakbyś użyła *git rm*, a następnie *git add*. Git poszukuje heurystycznie zmian nazw w następujących po sobie wersjach kopii. Dodatkowo potrafi czasami nawet znaleźć całe bloki z kodem przenoszonym tam i z powrotem między plikami! Mimo iż wykonuje kawał dobrej roboty, a ta właściwość staje się coraz lepsza, nie potrafi niestety jeszcze poradzić sobie z wszystkimi możliwymi przypadkami. Jeśli to u ciebie nie działa, spróbuj poszukać opcji rozszerzonego rozpoznawania kopii, aktualizacja samego Gita, też może pomóc. === Indeksowanie === Dla każdego kontrolowanego pliku, Git zapamiętuje informacje o jego wielkości, czasie utworzenia i czasie ostatniej edycji w pliku znanym nam jako indeks. By ustalić, czy nastąpiła jakaś zmiana, Git porównuje stan aktualny ze stanem zapamiętanym w indeksie. Jeśli dane te nie różnią się, Git może pominąć czytanie zawartości pliku. Ponieważ sprawdzenie statusu pliku trwa dużo krócej niż jego całkowite wczytanie, to jeśli dokonałaś zmian tylko na kilku plikach Git zaktualizuje swój stan w mgnieniu oka. Stwierdziliśmy już wcześniej, że indeks jest przechowalnią (ang. staging area). Jak to możliwe, że stos informacji o statusie danych może być przechowalnią? Ponieważ polecenie 'add' transportuje pliki do bazy danych Git i aktualizuje informacje o ich statusie, podczas gdy polecenie 'commit' (bez opcji) tworzy commit tylko wyłącznie na podstawie informacji o statusie plików, ponieważ pliki te już się w tej bazie znajdują. === Korzenie Git === Ten http://lkml.org/lkml/2005/4/6/121['Linux Kernel Mailing List' post] opisuje cały łańcuch zdarzeń, które inicjowały powstanie Git. Cały post jest archeologicznie fascynującą stroną dla historyków zajmujących się Gitem. === Obiektowa baza danych === Każda wersja twoich danych jest przechowywana w obiektowej bazie danych, która znajduje się w podkatalogu `.git/objects`. Inne miejsca w `.git/` posiadają mniej ważne dane, jak indeks, nazwy gałęzi ('branch'), tagi, logi, konfigurację, aktualną pozycję HEAD i tak dalej. Obiektowa baza danych jest prosta, mimo to jednak elegancka i jest źródłem siły Gita. Każdy plik w `.git/objects` jest obiektem. Istnieją trzy rodzaje obiektów, które nas interesują: 'blob', 'tree' i 'commit'. === Bloby === Na początek magiczna sztuczka. Wymyśl jakąś nazwę pliku, jakąkolwiek. W pustym katalogu: $ echo sweet > TWOJA_NAZWA $ git init $ git add . $ find .git/objects -type f $ find .git/objects -type f Zobaczysz coś takiego: +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. Skąd mogłem to wiedzieć, mimo iż nie znałem nazwy pliku? Ponieważ suma kontrolna SHA1 dla: "blob" SP "6" NUL "sweet" LF wynosi właśnie: aa823728ea7d592acc69b36875a482cdf3fd5c8d. Przy czym SP to spacja, NUL - to bajt zerowy, a LF to znak nowej linii ('newline'). Możesz to skontrolować wpisując: $ printf "blob 6\000sweet\n" | sha1sum Git pracuje asocjacyjnie (skojarzeniowo): dane nie są zapamiętywane na podstawie ich nazwy, tylko wartości ich własnego hasha SHA1 w pliku, który określamy mianem obiektu 'blob'. Sumę kontrolną SHA1 możemy sobie wyobrazić jako niepowtarzalny numer identyfikacyjny zawartości pliku, co oznacza, że pliki adresowane są na podstawie ich zawartości. Początkowe `blob 6`, to jedynie adnotacja, która określa tylko rodzaj obiektu i jego wielkość w bajtach, pozwala to na uproszczenie zarządzania wewnętrznego. Przez to właśnie mogłem 'przepowiedzieć' wynik. Nazwa pliku nie ma znaczenia, jedynie jego zawartość służy do utworzenia obiektu 'blob'. Pytasz się, a co w przypadku identycznych plików? Spróbuj dodać kopie twojej danej pod jakąkolwiek nazwą. Zawartość +.git/objects+ nie zmieni się, niezależnie ile kopii dodałaś. Git zapamięta zawartość pliku wyłącznie jeden raz. Na marginesie, dane w +.git/objects+ są spakowane poprzez 'zlib', nie powinieneś otwierać ich bezpośrednio. Przefiltruj je najpierw przez http://www.zlib.net/zpipe.c[zpipe -d], albo wpisz: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d polecenie to pokaże ci zawartość obiektu jako tekst. === 'Trees' === Gdzie są więc nazwy plików? Przecież muszą być gdzieś zapisane. Podczas wykonywania 'commit' Git troszczy się o nazwy plików: $ git commit # dodaj jakiś opis. $ find .git/objects -type f $ find .git/objects -type f Powinieneś ujrzeć teraz 3 obiekty. Tym razem nie jestem w stanie powiedzieć, jak nazywają się te dwa nowe pliki, ponieważ częściowo są zależne od nazwy jaką nadałaś plikom. Pójdźmy dalej, zakładając, że jedną z tych danych nazwałaś ``rose''. Jeśli nie, możesz zmienić opis, by wyglądał jakby był twój: $ git filter-branch --tree-filter 'mv TWOJA_NAZWA rose' $ find .git/objects -type f Powinnaś zobaczyć teraz plik +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, ponieważ jest to suma kontrolna SHA1 jego zawartości. "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Sprawdź, czy plik na prawdę odpowiada powyższej zawartości przez polecenie: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch Za pomocą 'zpipe' łatwo sprawdzić hash SHA1: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum Sprawdzanie za pomocą 'cat-file' jest troszeczkę kłopotliwe, bo jego 'output' zawiera więcej niż tylko nieskomprymowany obiekt pliku. Nasz plik to tak zwany obiekt 'tree': lista wyrażeń, na którą składają się rodzaj pliku, jego nazwa i jego suma kontrolna SHA1. W naszym przykładzie typ pliku to 100644, co oznacza, że `rose` jest plikiem zwykłym, natomiast hash SHA1 odpowiada sumie kontrolnej SHA1 obiektu 'blob' zawierającego zawartość `rose`. Inne możliwe rodzaje plików to programy, linki symboliczne i katalogi. W ostatnim przypadku hash SHA1 wskazuje na obiekt 'tree'. Jeśli użyjesz polecenia 'filter-branch', otrzymasz stare objekty, które nie są już używane. Mimo iż automatycznie zostaną usunięte po upłynięciu okresu karencji, chcemy się ich pozbyć od zaraz, aby lepiej prześledzić następne przykłady. $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune W prawdziwych projektach powinnaś unikać takich komend, ponieważ zniszczą zabezpieczone dane. Jeśli chcesz posiadać czyste repozytorium, to najlepiej załóż nowy klon. Bądź też ostrożna przy bezpośredniej manipulacji +.git+: gdy równocześnie wykonywane jest polecenie Git i zgaśnie światło? Generalnie do kasowania referencji powinnaś używać *git update-ref -d*, nawet gdy ręczne usunięcie +ref/original+ jest dość bezpieczne. === 'Commits' === Wytłumaczyliśmy dwa z trzech obiektów. Ten trzeci to obiekt 'commit' Jego zawartość jest zależna od opisu 'commit' jak i czasu jego wykonania. By wszystko do naszego przykładu pasowało, musimy trochę pokombinować. $ git commit --amend -m Shakespeare # Zmień ten opis. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Zmanipuluj znacznik czasowy i nazwę autora. $ find .git/objects -type f $ find .git/objects -type f Powinieneś znaleźć +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+, co odpowiada sumie kontrolnej SHA1 jego zawartości: "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice <alice@example.com> 1234567890 -0800" LF "committer Bob <bob@example.com> 1234567890 -0800" LF LF "Shakespeare" LF Jak i w poprzednich przykładach możesz użyć 'zpipe' albo 'cat-file' by to sprawdzić. To jest pierwszy 'commit', przez to nie posiada matczynych 'commits'. Następujące 'commits' będą zawsze zawierać przynajmniej jedną linikę identyfikującą rodzica. === Nie do odróżnienia od magii === Tajemnice Gita wydają się być proste. Wygląda to jak połączenie kilku skryptów, troszeczkę kodu C i w przeciągu kilku godzin jesteśmy gotowi: zmiksowanie podstawowych operacji na systemie danych, obliczenia SHA1, przyprawienie plikami blokującymy i synchronizacją dla stabilności. W sumie można by tak opisać najwcześniejsze wersje Gita. Tym niemniej, abstrahując od udanych trików pakujących, by oszczędnie odnosić się z pamięcią i udanych trików indeksujących by zaoszczędzić czas, wiemy jak Git sprawnie przemienia system danych w objektową bazę danych, co jest optymalne dla kontroli wersji. Przyjmijmy, gdy jakikolwiek plik w obiektowej bazie danych ulegnie zniszczeniu poprzez błąd nośnika, to jego SHA1 nie będzie zgadzać się z jego zawartością, co od razu wskaże nam problem. Poprzez tworzenie kluczy SHA1 z kluczy SHA1 innych objektów, osiągniemy integralność danych na wszystkich poziomach. 'Commits' są elementarne, to znaczy, 'commit' nie potrafi zapamiętać jedynie części zmian: hash SHA1 'commit' możemy obliczyć i zapamiętać dopiero po tym gdy zapamiętane zostały wszystkie obiekty 'tree', 'blob' i rodziców 'commit'. Obiektowa baza dynch jest odporna na nieoczekiwane przerwy, jak na przykład przerwanie dostawy prądu. Możemy przetrwać nawet podstępnego przeciwnika. Wyobraź sobie, ktoś ma zamiar zmienić treść jakiegoś pliku, która leży w jakiejś starszej wersji projektu. By sprawić pozory, że baza danych wygląda nienaruszona musiałby zmienić sumy kontrolne SHA1 korespondujących obiektów, ponieważ plik zawiera teraz zmieniony sznur znaków. To znaczy również, że musiałby zmienić każdy hash obiektu 'tree', które ją referują oraz w wyniku tego wszystkie sumy kontrolne 'commits' zawierające obiekty 'tree' dodatkowo do pochodnych tych 'commits'. Oznacza to również, że suma kontrolna oficjalnego HEAD różni się od sumy kontrolnej HEAD manipulowanego repozytorium. Wystarczy teraz prześledzić ścieżkę różniących się hashy SHA1, odnaleźć okaleczony plik, jak i 'commit' w którym po raz pierwszy wystąpił. Krótko mówiąc, dopuki reprezentujące ostatni commit 20 bajtów są zabezpieczone, sfałszowanie repozytorium Gita nie jest możliwe. A co ze sławnymi możliwościami Gita? 'Branching'? 'Merging'? 'Tags'? To szczegół. Aktualny HEAD przetrzymywany jest w pliku +.git/HEAD+, która posiada hash SHA1 ostatniego 'commit'. Hash SHA1 zostaje aktualizowany podczas wykonania 'commit', tak samo jak i przy wielu innych poleceniach. 'branches' to prawie to samo, są plikami zapamiętanymi w +.git/refs/heads+. 'Tags' również, znajdziemy je w +.git/refs/tags+, są one jednak aktualizowane poprzez serię innych poleceń. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pl/multiplayer.txt����������������������������������������������������������������0000644�0001750�0001750�00000023216�12666307504�016527� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Multiplayer Git == Na początku zastosowałem Git w prywatnym projekcie, gdzie byłem jedynym programistą. Z poleceń, w związku z rozproszoną naturą Gita, potrzebowałem jedynie komende *pull* i *clone*, dzięki czemu mogłem trzymać ten sam projekt w kilku miejscach. Później chciałem opublikować mój kod za pomocą Gita i dołączyć zmiany kolegów. Musiałem nauczyć się zarządzać projektami, nad którymi zaangażowani byli programiści z całego świata. Na szczęście jest to silną stroną Gita i chyba jego racją bytu. === Kim jestem? === Każdy 'commit' otrzymuje nazwę i adres e-mail autora, które zostaną pokazane w *git log*. Standardowo Git korzysta z ustawień systemowych do wypełnienia tych pól. Aby wprowadzić te dane bezpośrednio, podaj: $ git config --global user.name "Jan Kowalski" $ git config --global user.email jan.kowalski@example.com Jeśli opóścisz przełącznik '--global' zmiany zostaną zastosowane wyłącznie do aktualnego repozytorium. === Git przez SSH, HTTP === Załóżmy, posiadasz dostęp SSH do serwera stron internetowych, gdzie jednak Git nie został zainstalowany. Nawet, jeśli jest to mniej efektywne jak rodzimy protokół 'GIT', Git potrafi komunikować się również przez HTTP. Zładuj Git, skompiluj i zainstaluj pod własnym kontem oraz utwórz repozytorium w twoim katalogu strony internetowej. $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update Przy starszych wersjach Gita samo polecenie 'cp' nie wystarczy, wtedy musisz jeszcze: $ chmod a+x hooks/post-update Od teraz możesz publikować aktualizacje z każdego klonu poprzez SSH. $ git push web.server:/sciezka/do/proj.git master i każdy może teraz sklonować twój projekt przez HTTP: $ git clone http://web.server/proj.git === Git ponad wszystko === Chciałbyś synchronizować repozytoria bez pomocy serwera czy nawet bez użycia sieci komputerowej? Musisz improwizować w nagłym wypadku? Widzieliśmy, że poleceniami <<makinghistory, *git fast-export* i *git fast-import* możemy konwertować całe repozytoria w jeden jedyny plik i z powrotem>>. W ten sposób możemy transportować tego typu pliki za pomocą dowolnego medium, jednak bardziej wydajnym narzędziem jest *git bundle*. Nadawca tworzy 'bundle': $ git bundle create plik HEAD i transportuje 'bundle' +plik+ do innych zaangażowanych: przez e-mail, pendrive, *xxd* hexdump i skaner OCR, kod morsea, przez telefon, znaki dymne, itd. Odbiorca wyciąga 'commits' z 'bundle' poprzez podanie: $ git pull plik Odbiorca może to zrobić z pustym repozytorium. Mimo swojej wielkości +plik+ zawiera kompletny oryginał repozytorium. W dużych projektach unikniesz śmieci danych, jeśli tylko zrobisz 'bundle' zmian brakujących w innych repozytoriach. Na przykład załóżmy, że 'commit' ``1b6d...'' jest najaktualniejszym, które posiadają obie partie: $ git bundle create plik HEAD ^1b6d Jeśli robi się to regularnie, łatwo można zapomnieć, który 'commit' został wysłany ostatnio. Strony pomocy zalecają stosowanie tagów, by rozwiązać ten problem. To znaczy, po wysłaniu 'bundle', podaj: $ git tag -f ostatni_bundle HEAD a nowy 'bundle' tworzymy następnie poprzez: $ git bundle create nowy_bundle HEAD ^ostatni_bundle === Patches: globalny środek płatniczy === 'Patches' to jawne zobrazowanie twoich zmian, które mogą być jednocześnie rozumiane przez komputer i człowieka. Dodaje im to uniwersalnej mocy przyciągania. Możesz wysłać patch prowadzącym projekt, niezależnie od tego, jakiego używają systemu kontroli wersji. Doputy twoi współpracownicy potrafią czytać swoje maile, mogą widzieć również twoje zmiany. Również i z twojej strony wszystko, czego ci potrzeba to funkcjonujące konto e-mailowe: nie istnieje konieczność zakładania repozytorium online. Przypomnij sobie pierwszy rozdział: $ git diff 1b6d > mój.patch produkuje 'patch', który można dołączyć do e-maila dla dalszej dyskusji. W repozytorium Git natomiast podajesz: $ git apply < mój.patch By zastosować patch. W bardziej oficjalnym środowisku, jeśli nazwiska autorów i ich sygnatury powinny również być notowane, twórz 'patch' od pewnego punktu, po wpisaniu: $ git format-patch 1b6d Uzyskane w ten sposób dane mogą przekazane być do *git-send-mail* albo odręcznie wysłane. Możesz podać grupę 'commits' $ git format-patch 1b6d..HEAD^^ Po stronie odbiorcy zapamiętaj e-mail jako daną i podaj: $ git am < email.txt Patch zostanie wprowadzony i utworzy commit, włącznie z informacjami jak na przykład informacje o autorze. Jeśli stosujesz webmail musisz ewentualnie poszukać opcji pokazania treści w formie niesformatowanego textu, zanim zapamiętasz patch do pliku. Występują minimalne różnice między aplikacjami e-mailowymi bazującymi na mbox, ale jeśli korzystasz z takiej, należysz do grupy ludzi, która za pewne umie się z nimi obchodzić bez czytania instrukcji! === Przepraszamy, przeprowadziliśmy się === Po sklonowaniu repozytorium, polecenia *git push* albo *git pull* będą automatycznie wskazywały na oryginalne URL. Jak Git to robi? Tajemnica leży w konfiguracji, która utworzona zostaje podczas klonowania. Zaryzykujmy spojrzenie: $ git config --list Opcja +remote.origin.url+ kontroluje źródłowe URL; ``origin'' to alias, nadany źródłowemu repozytorium. Tak jak i przy konwencji z 'master branch', możemy ten alias zmienić albo skasować, zwykle jednak nie ma powodów by to robić. Jeśli oryginalne repozytorium zostanie przesunięte, możemy zaktualizować link poprzez: $ git config remote.origin.url git://nowy_link/proj.git Opcja +branch.master.merge+ definiuje standardowy 'remote-branch' dla *git pull*. Podczas początkowego klonowania, zostanie ustawiony na aktualny branch źródłowego repozytorium, że nawet i po tym jak 'HEAD' źródłowego repozytorium przejdzie do innego branch, późniejszy 'pull' pozostanie wierny oryginalnemu branch. Ta opcja jest ważna jedynie dla repozytorium, z którego dokonało się pierwszego klonowania, co zapisane jest w opcji +branch.master.remote+. Przy 'pull' z innego repozytorium musimy podać z którego branch chcemy korzystać. $ git pull git://example.com/inny.git master To wyjaśnia dlaczego nasze poprzednie przykłady z 'push' i 'pull' nie posiadały argumentów. === Oddalone 'Branches' === Jeśli klonujesz repozytorium, klonujesz również wszystkie jego 'branches' Może jeszcze tego nie zauważyłaś, ponieważ Git je ukrywa: musisz się o nie specjalnie pytać: To zapobiega temu, że branches z oddalonego repozytorium nie przeszkadzają twoim lokalnym branches i czyni to Git łatwiejszym dla początkujących. Oddalone 'branches' możesz pokazać poprzez: $ git branch -r Powinieneś zobaczyć coś jak: origin/HEAD origin/master origin/experimental Lista ta ukazuje branches i HEAD odległego repozytorium, które mogą być również stosowane w zwykłych poleceniach Git. Przyjmijmy, na przykład, że wykonałaś wiele commits i chciałbyś uzyskać porównanie do ostatnio ściągniętej wersji. Możesz przeszukać logi za odpowiednim hashem SHA1, ale dużo prościej jest podać: $ git diff origin/HEAD Możesz też sprawdzić co działo się w 'branch' ``experimental'': $ git log origin/experimental === Więcej serwerów === Przyjmijmy, dwóch innych programistów pracuje nad twoim projektem i chciałabyś mieć ich na oku. Możemy obserwować więcej niż jedno repozytorium jednocześnie: $ git remote add inny git://example.com/jakies_repo.git $ git pull inny jakis_branch Teraz przyłączyliśmy jeden 'branch' z dwóch repozytoriów i uzyskaliśmy łatwy dostęp do wszystkich 'branch' z wszystkich repozytoriów. $ git diff origin/experimental^ inny/jakiś_branch~5 Co jednak zrobić, gdy chcemy porównać zmiany w nich bez wpływu na naszą pracę? Innymi słowami, chcemy zbadać ich 'branches' bez importowania ich zmian do naszego katalogu roboczego. Zamiast 'pull' skorzystaj z: $ git fetch # Fetch z origin, standard. $ git fetch inne # Fetch od drugiego programisty. Polecenie to załaduje jedynie historię Mimo, że nasz katalog pozostał bez zmian, możemy teraz referować z każdego repozytorium poprzez polecenia Gita, ponieważ posiadamy lokalną kopię. Przypomnij sobie, że 'pull' za kulisami to to samo co 'fetch' z następującym za nim *merge*. W normalnym wypadku wykonalibyśmy *pull*, bo chcielibyśmy przywołać również ostatnie 'commmits'. Ta przywołana sytuacja jest wyjątkiem wartym wspomnienia. Sprawdź *git help remote* by zobaczyć, jak usuwa się repozytoria, ignoruje pewne branches i więcej. === Moje ustawienia === W moich projektach preferuję, gdy pomagający mi programiści przygotują własne repozytoria z których mogę wykonać 'pull'. Większość hosterów Gita pozwala na utworzenie jednym kliknięciem twojego własnego forka innego projektu. Gdy przywołałem moją gałęź korzystam z poleceń Gita dla nawigacji i kontroli zmian, które najlepiej, gdy są dobrze zorganizowane i udokumentowane. Wykonuję 'merge' moich własnych zmian i przeprowadzam ewentualnie dalsze zmiany. Gdy już jestem zadowolony, 'push' do zentralnego repozytorium. Mimo, iż dość rzadko otrzymuję posty, jestem zdania, że ta metoda się opłaca. Zobacz http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[Post na Blogu Linusa Torvalds (po angielsku)]. Pozostając w świecie Gita jest wygodniejsze niż otrzymywanie patchów, ponieważ zaoszczędza mi to konwertowanie ich do 'commits' Gita. Poza tym Git martwi się o szczegóły, jak nazwa autora i adres e-maila, tak samo jak i o datę i godzinę oraz motywuje autora do opisywania swoich zmian. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pl/preface.txt��������������������������������������������������������������������0000644�0001750�0001750�00000010221�12666307504�015555� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������= Git Magic = Ben Lynn Sierpień 2007 == Przedmowa == http://git-scm.com/[Git] to rodzaj scyzoryka szwajcarskiego dla kontroli wersji. To niezawodne, wielostronne narzędzie do kontroli wersji o niezwykłej elastyczności przysprawia trudności już w samym jego poznaniu, nie wspominając o opanowaniu. Jak stwierdził Arthur C. Clarke, każda wystarczająco postępowa technologia jest porównywalna z magią. Jest to wspaniałe podejście, by zacząć pracę z Gitem: Początkujący mogą zignorować jego wewnętrzne mechanizmy i ujrzeć jako rzecz, która urzeka przyjaciół swoimi niezwykłymi możliwościami, a przeciwników doprowadza do białej gorączki. Zamiast wchodzić w szczegóły, oferujemy proste instrukcje dla osiągnięcia zamierzonych efektów. W miarę regularnego korzystania stopniowo sam zrozumiesz jak każda z tych sztuczek funkcjonuje i jak dopasować podane instrukcje na twoje własne potrzeby. .Tłumaczenia - link:/\~blynn/gitmagic/intl/zh_cn/[Chiński uproszczony]: od JunJie, Meng i JiangWei. Konwertacja do link:/~blynn/gitmagic/intl/zh_tw/[Tradycjonalnego chińskiego] za pomocą +cconv -f UTF8-CN -t UTF8-TW+. - link:/~blynn/gitmagic/intl/fr/[Francuski]: od Alexandre Garel, Paul Gaborit, i Nicolas Deram. Również hostowany na http://tutoriels.itaapy.com/[itaapy]. - link:/~blynn/gitmagic/intl/de/[Niemiecki]: od Benjamin Bellee i Armin Stebich; również hostowany na http://gitmagic.lordofbikes.de/[stronie Armina]. - http://www.slideshare.net/slide_user/magia-git[Portugalski]: od Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[wersja ODT]]. - link:/~blynn/gitmagic/intl/ru/[Rosyjski]: od Tikhon Tarnavsky, Mikhail Dymskov, i innych. - link:/~blynn/gitmagic/intl/es/[Hiszpański]: od Rodrigo Toledo i Ariset Llerena Tapia. - link:/~blynn/gitmagic/intl/uk/[Ukraiński]: od Volodymyr Bodenchuk. - link:/~blynn/gitmagic/intl/vi/[Wietnamski]: od Trần Ngọc Quân; również hostowany http://vnwildman.users.sourceforge.net/gitmagic/[na tej stronie]. .Inne wydania - link:book.html[Jako jedna strona]: uszczuplone HTML, bez CSS. - link:book.pdf[Wersja PDF]: przyjazna w druku. - http://packages.debian.org/gitmagic[Pakiet Debiana], http://packages.ubuntu.com/gitmagic[Pakiet Ubuntu]: Pobiera szybką i lokalną kopię tej strony. Przydatne http://csdcf.stanford.edu/status/[gdyby ten serwer był offline]. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Drukowane wydanie [Amazon.com]]: 64 strony, 15.24cm x 22.86cm, czarno-biały. Przyda się, gdy zabraknie prądu. === Podziękowania! === Jestem mile zaskoczony, że tak dużo ludzi pracowało nad przetłumaczeniem tych stron. Bardzo cenię, iż dzięki staraniom wyżej wspommnianych osób otrzymałem możliwość dotarcia do większego grona czytelników. Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, i Tyler Breisacher przyczynilli się do poprawek i korektur. François Marier jest mentorem pakietu Debiana, który uprzednio utworzony został przez Daniela Baumann. Chciałbym podziękować również wszystkim innym za ich pomoc i dobre słowo. Chciałbym tu wszystkich wyszczególnić, mogłoby to jednak wzbudzić oczekiwania w szerokim zakresie. Gdybym o tobie przypadkowo zapomniał, daj mi znać albo przyślij mi po prostu patch. === Licencja === Ten poradnik publikowany jest na bazie licencji http://www.gnu.org/licenses/gpl-3.0.html[GNU General Public License Version 3]. Oczywiście, tekst źródłowy znajduje się w repozytorium Git i może zostać pobrany przez: $ git clone git://repo.or.cz/gitmagic.git # Utworzy katalog "gitmagic". albo z serwerów lustrzanych: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git $ git clone https://code.google.com/p/gitmagic/ $ git clone git://git.assembla.com/gitmagic.git $ git clone git@bitbucket.org:blynn/gitmagic.git GitHub, Assembla, i Bitbucket pozwalają na prowadzienie prywatnych repozytorii, te dwa ostatnie za darmo. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pl/basic.txt����������������������������������������������������������������������0000644�0001750�0001750�00000020566�12666307504�015246� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Pierwsze kroki == Zanim utoniemy w morzu poleceń Gita, przyjrzyjmy się najpierw kilku prostym poleceniom. Pomimo ich prostoty, wszystkie jednak są ważne i pożyteczne. W rzeczywistości, podczas pierwszych miesięcy pracy z Git nie wychodziłem poza zakres opisany w tym rozdziale === Zabezpieczenie obecnego stanu === Zamierzasz przeprowadzić jakieś drastyczne zmiany? Zanim to zrobisz, zabezpiecz najpierw dane w aktualnym katalogu. $ git init $ git add . $ git commit -m "Mój pierwszy commit" Teraz, jeśli cokolwiek stałoby się z twymi plikami podczas edycji, możesz przywrócić pierwotną wersję: $ git reset --hard Aby zapisać nową wersję: $ git commit -a -m "Mój następny commit" === Dodanie, kasowanie i zmiana nazwy === Powyższa komenda zatrzyma jedynie pliki, które już istniały podczas gdy po raz pierwszy wykonałaś polecenie *git add*. Jeśli w międzyczasie dodałaś jakieś nowe pliki, Git musi zostać o tym poinformowany: $ git add readme.txt Dokumentacja To samo, gdy zechcesz by Git zapomniał o wybranych plikach: $ git rm ramsch.h archaiczne.c $ git rm -r obciążający/materiał/ Jeśli sam tego jeszcze nie zrobiłaś, to Git usunie pliki za ciebie. Zmiana nazwy pliku, to jak jego skasowanie i ponowne utworzenie z nową nazwą. Git wykorzystuje do tego skrót *git mv*, który posiada tą samą składnię co polecenie *mv*. Na przykład: $ git mv bug.c feature.c === Zaawansowane anulowanie/przywracanie === Czasami zechcesz po prostu cofnąć się w czasie, zapominając o wszystkich wprowadzonych od tego punktu zmianach. Wtedy: $ git log pokaże ci listę dotychczasowych 'commits' i ich sum kontrolnych SHA1: ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob <bob@example.com> Date: Tue Mar 14 01:59:26 2000 -0800 Zamień printf() na write(). commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alicja <alicja@example.com> Date: Thu Jan 1 00:00:00 1970 +0000 Initial commit. ---------------------------------- Kilka początkowych znaków sumy kontrolnej SHA1 wystarcza by jednoznacznie zidentyfikować 'commit', alternatywnie możesz skopiować i wkleić cały hash. Wpisując: $ git reset --hard 766f przywrócisz stan do wersji żądanego 'commit', a wszystkie późniejsze zmiany zostaną bezpowrotnie skasowane. Innym razem chcesz tylko na moment przejść do jednej z poprzednich wersji. W tym wypadku użyj komendy: $ git checkout 82f5 Tym poleceniem wrócisz się w czasie zachowując nowsze zmiany. Ale, tak samo jak w podróżach w czasie z filmów science-fiction - jeśli teraz dokonasz zmian i zapamiętasz je poleceniem 'commit', zostaniesz przeniesiona do alternatywnej rzeczywistości, ponieważ twoje zmiany różnią się od już dokonanych w późniejszych punktach czasu. Tą alternatywną rzeczywistość nazywamy 'branch', a <<branch,zajmiemy się tym w późniejszym czasie>>. Na razie, zapamiętaj tylko, że: $ git checkout master sprowadzi cię znów do teraźniejszości. Również, aby uprzedzić narzekanie Gita, powinnaś przed każdym 'checkout' wykonać 'commit' lub 'reset'. Korzystając ponownie z analogii do gier komputerowych: - *`git reset --hard`*: załaduj jakiś starszy stan gry i skasuj wszystkie nowsze niż właśnie ładowany. - *`git checkout`*: Załaduj stary stan, grając dalej, twój stan będzie się różnił od nowszych zapamiętanych. Każdy stan, który zapamiętasz od teraz, powstanie jako osobna gałęź ('branch'), reprezentującym alternatywną rzeczywistość. <<branch,Wrócimy do tego później>> Jeśli chcesz, możesz przywrócić jedynie wybrane pliki lub katalogi poprzez dodanie ich nazw do polecenia: $ git checkout 82f5 jeden.plik inny.plik Bądź ostrożna, ten sposób użycia komendy *checkout* może bez uprzedzenia skasować pliki. Aby zabezpieczyć się przed takimi wypadkami powinnaś zawsze zrobić 'commit' zanim wpiszesz 'checkout', szczególnie w okresie poznawania Gita. Jeśli czujesz się niepewnie przed wykonaniem jakiejś operacji Gita, generalną zasadą powinno stać się dla ciebie uprzednie wykonanie *git commit -a*. Nie lubisz kopiować i wklejać hashów SHA1? Możesz w tym wypadku skorzystać z: $ git checkout :/"Mój pierwszy c" by przenieś się do 'commit', którego opis rozpoczyna się jak zawarta wiadomość. Możesz również cofnąć się do piątego z ostatnio zapamiętanych 'commit': $ git checkout master~5 === Przywracanie === W sali sądowej pewne zdarzenia mogą zostać wykreślone z akt. Podobnie możesz zaznaczyć pewne 'commits' do wykreślenia. $ git commit -a $ git revert 1b6d To polecenie wymaże 'commit' o wybranym hashu. Ten rewers zostanie zapamiętany jednak jako nowy 'commit', co można sprawdzić poleceniem *git log*. === Generowanie listy zmian === Niektóre projekty wymagają http://en.wikipedia.org/wiki/Changelog[pliku changelog]. Wygenerujesz go poleceniem: $ git log > changelog === Ładowanie plików === Kopię projektu zarządzanego za pomocą Gita uzyskasz poleceniem: $ git clone git://ścieżka/do/projektu By na przykład zładować wszystkie dane, których użyłem do stworzenia tej strony skorzystaj z: $ git clone git://git.or.cz/gitmagic.git Do polecenia 'clone' wrócimy niebawem. === Najnowszy stan === Jeśli posiadasz już kopię projektu wykonaną za pomocą *git clone*, możesz ją zaktualizować poleceniem: $ git pull === Szybka publikacja === Przypuśćmy, że napisałaś skrypt i chcesz go udostępnić innym. Mogłabyś poprosić ich, by zładowali go bezpośrednio z twojego komputera. Jeśli jednak zrobią to podczas gdy ty jeszcze wprowadzasz poprawki lub eksperymentujesz ze zmianami, mogłabyś przysporzyć im nieprzyjemności. Z tego powodu istnieje coś takiego jak cykl wydawniczy. Programiści regularnie pracują nad projektem, upubliczniają kod jednak dopiero, jeśli uznają, że nadaje się już do pokazania. Aby wykonać to za pomocą GIT, wejdź do katalogu w którym znajduje się twój skrypt: $ git init $ git add . $ git commit -m "Pierwsze wydanie" Następnie poproś twych użytkowników o wykonanie: $ git clone twój.komputer:/ścieżka/do/skryptu by zładować twój skrypt. Zakładamy tu posiadanie przez nich klucza SSH do twojego komputera. Jeśli go nie mają, uruchom *git daemon* i podaj im następujący link: $ git clone git://twój.komputer/ścieżka/do/skryptu Od teraz, zawsze gdy uznasz, że wersja nadaje się do opublikowania, wykonaj polecenie: $ git commit -a -m "Następna wersja" a twoi użytkownicy, po wejściu do katalogu zawierającego twój skrypt, będą go mogli zaktualizować poprzez: $ git pull Twoi użytkownicy nigdy nie wejdą w posiadanie wersji, których nie chcesz im udostępniać. === A co robiłem ostatnio? === Jeśli chcesz zobaczyć zmiany, które wprowadziłaś od ostatniego 'commit', wpisz: $ git diff Albo tylko zmiany od wczoraj: $ git diff "@{yesterday}" Albo miedzy określoną wersją i dwoma poprzedzającymi: $ git diff 1b6d "master~2" Za każdym razem uzyskane informacje są równocześnie patchem, który poprzez *git apply* może być zastosowany. Spróbuj również: $ git whatchanged --since="2 weeks ago" Jeśli chcę sprawdzić listę zmian jakiegoś repozytorium, często korzystam z http://sourceforge.net/projects/qgit[qgit], ze względu na jego fotogeniczny interfejs, albo z http://jonas.nitro.dk/tig/[tig], tekstowy interfejs, działający zadowalająco gdy mamy do czynienia z wolnym łączem internetowym. Alternatywnie, zainstaluj serwer HTTP, uruchom *git instaweb* i odpal dowolną przeglądarkę internetową. === Ćwiczenie === Niech A, B, C i D będą 4 następującymi po sobie 'commits', gdzie B różni się od A, jedynie tym, iż usunięto kilka plików. Chcemy teraz te usunięte pliki zrekonstruować do D. Jak to można zrobić? Istnieją przynajmniej 3 rozwiązania. Załóżmy że znajdujemy się obecnie w D: 1. Różnica pomiędzy A i B, to skasowane pliki. Możemy utworzyć patch, który pokaże te różnice i następnie zastosować go: $ git diff B A | git apply 2. Ponieważ pliki zostały już raz zapamiętane w A, możemy je przywrócić: $ git checkout A foo.c bar.h 3. Możemy też widzieć przejście z A na B jako zmianę, którą można zrewertować: $ git revert B A które z tych rozwiązań jest najlepsze? To, które najbardziej tobie odpowiada. Korzystając z Git łatwo osiągnąć cel, czasami prowadzi do niego wiele dróg. ������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pl/grandmaster.txt����������������������������������������������������������������0000644�0001750�0001750�00000027455�12666307504�016500� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Git dla zaawansowanych == W międzyczasie powinnaś umieć odnaleźć się na stronach *git help* i rozumieć większość zagadnień. Mimo to może okazać się dość mozolne odnalezienie odpowiedniej komendy dla rozwiązania pewnego zadania. Może uda mi się zaoszczędzić ci trochę czasu: poniżej znajdziesz kilka przepisów, które były mi przydatne w przeszłości. === Publikowanie kodu źródłowego === Git zarządza w moich projektach dokładnie tymi danymi, które chcę archiwizować i dać do dyspozycji innym użytkownikom. Aby utworzyć archiwum tar kodu źródłowego, używam polecenia: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === Zmiany dla 'commit' === Powiadomienie Gita o dodaniu, skasowaniu czy zmianie nazwy plików może okazać sie przy niektórych projektach dość uciążliwą pracą. Zamiast tego można skorzystać z: $ git add . $ git add -u Git przyjży się danym w aktualnym katalogu i odpracuje sam szczegóły. Zamiast tego drugiego polecenia możemy użyć `git commit -a`, jeśli i tak mamy zamiar przeprowadzić 'comit'. Sprawdź też *git help ignore*, by dowiedzieć się jak zdefiniować dane, które powinny być zignorowane. Można to także wykonać za jednyym zamachem: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove Opcje *-z* i *-0* zapobiegą przed niechcianymi efektmi ubocznymi przez niestandardowe znaki w nazwach plików. Ale ponieważ to polecenie dodaje również pliki które powinny być zignorowane, można dodać do niego jeszcze opcje `-x` albo `-X` === Mój 'commit' jest za duży! === Od dłuższego czasu nie pamiętałaś o wykonaniu 'commit'? Tak namiętnie programowałaś, że zupełnie zapomniałaś o kontroli kodu źródłowego? Przeprowadzasz serię niezależnych zmian, bo jest to w twoim stylu? Nie ma sprawy, wpisz polecenie: $ git add -p Dla każdej zmiany, której dokonałaś Git pokaże ci pasaże z kodem, który uległ zmianom i spyta cię, czy mają zostać częścią następnego 'commit'. Odpowiedz po prostu "y" dla tak, albo "n" dla nie. Dysponujesz jeszcze innymi opcjami, na przykład dla odłożenie w czasie decyzji, wpisz "?", by dowiedzieć się więcej. Jeśli jesteś już zadowolony z wyniku, wpisz: $ git commit by dokonać wybranych zmian. Uważaj tylko, by nie skorzystać z opcji *-a*, ponieważ wtedy git dokona 'commit' zawierający wszystkie zmiany. A co, jeśli pracowałaś nad wieloma danymi w wielu różnych miejscach? Sprawdzenie każdej danej z osobna jest zarówno rustrujące jak i męczące. W takim wypadku skorzystaj z *git add -i*, obsługa tego polecenia może nie jest zbyt łatwa, za to jednak bardzo elastyczna. Kilkoma naciśnięciami klawiszy możesz wiele zmienionych plików dodać ('stage') albo usunąć z 'commit' ('unstage'), jak również sprawdzić, czy dodać zmiany dla poszczególnych plików. Alternaywnie możesz skorzystać z *git commit \--interactive*, polecenie to wykona automatycznie 'commit' gdy skończysz. === Index: rusztowanie Gita === Do tej pory staraliśmy się omijać sławny 'indeks', jednak przyszedł czas się nim zająć, aby móc wyjaśnić wszystko to co poznaliśmy do tej pory. Index jest tymczasową przechowalnią. Git rzadko wymienia dane bezpośrednio między twoim projektem a swoją historią wersji. Raczej zapisuje on dane najpierw w indeksie, dopiero po tym kopiuje dane z indeksu na ich właściwe miejsce przeznaczenia. Na przykład polecenie *commit -a* jest właściwie procesem dwustopniowym. Pierwszy krok to stworzenie obrazu bieżącego statusu każdego monitorowanego pliku do indeksu. Drugim krokiem jest trwałe zapamiętanie obrazu do indeksu. Wykonanie 'commit' bez opcji *-a* wykona jedynie drugi wspomniany krok i ma jedynie sens, jeśli poprzednio wykonano komendę, która dokonała odpowiednich zmian w indeksie, na przykład *git add*. Normalnie możemy ignorować indeks i udawać, że czytamy i zapisujemy bezpośrednio z historii. W tym wypadku chcemy posiadać jednak większą kontrolę, więc manipulujemy indeks. Tworzymy obraz niektórych, jednak nie wszystkich zmian w indeksie i później zapamiętujemy trwale starannie dobrany obraz. === Nie trać głowy! === Identyfikator 'HEAD' zachowuje się jak kursor, który zwykle wskazuje na najmłodszy 'commit' i z każdym nowym 'commit' zostaje przesunięty do przodu. Niektóre komendy Gita pozwolą ci nim manipulować. Na przyklad: $ git reset HEAD~3 przesunie identyfikator 'HEAD' o 3 'commits' spowrotem. Spowoduje to, że wszystkie następne komendy Gita będą reagować, jakby tych trzech ostatnich 'commits' wogóle nie było, podczas gdy twoje dane zostaną w przyszłości. Na stronach pomocy Gita znajdziesz więcej takich zastosowań. Ale jak teraz wrócić znów do przyszłości? Poprzednie 'commits' nic nie wiedzą o jej istnieniu. Jeśli posiadasz hash SHA1 orginalnego 'HEAD', wtedy możesz wrócić komendą: $ git reset 1b6d Wyobraź jednak sobie, że nigdy go nie notowałaś? Nie ma sprawy: Przy wykonywaniu takich poleceń Git archiwizuje orginalny HEAD jako indentyfikator o nazwie ORIG_HEAD a ty możesz bezproblemowo wrócić używając: $ git reset ORIG_HEAD === Łowcy głów === Może się zdarzyć, że ORIG_HEAD nie wystarczy. Może właśnie spostrzegłaś, iż dokonałaś kapitalnego błędu i musisz wrócić się do bardzo starego 'commit' w zapomnianym 'branch'. Standardowo Git zapamiętuje 'commit' przez przynajmniej 2 tygodnie, nawet jeśli poleciłaś zniszczyć 'branch' w którym istniał. Problemem staje się tutaj odnalezienie odpowieniej sumy kontrolnej SHA1. Możesz po kolei testować wszystkie hashe SHA1 w `.git/objects` i w ten sposób próbować odnaleźć szukany 'commit'. Istnieje jednak na to dużo prostszy sposób. Git zapamiętuje każdy obliczony hash SHA1 dla odpowiednich 'commit' w `.git/logs`. Podkatalog `refs` zawieza przebieg wszystkich aktywności we wszystkich 'branches', podczas gdy plik `HEAD` wszystkie klucze SHA1 które kiedykolwiek posiadał. Ostatnie możemy zastosować do odnalezienia sum kontrolnych SHA1 tych 'commits' które znajdowały się w nieuważnie usuniętym 'branch'. Polecenie 'reflog' daje nam do dyspozycji przyjazny interfejs do tych właśnie logów. Wypróbuj polecenie: $ git reflog Zamiast kopiować i wklejać klucze z 'reflog', możesz: $ git checkout "@{10 minutes ago}" Albo przywołaj 5 z ostatnio oddwiedzanych 'commits' za pomocą: $ git checkout "@{5}" Jeśli chciałbyś pogłębić wiedze na ten temat przeczytaj sekcję ``Specifying Revisions`` w *git help rev-parse*. Byś może zechcesz zmienić okres karencji dla przeznaczonych na stracenie 'commits'. Na przyklad: $ git config gc.pruneexpire "30 days" znaczy, że skasowany 'commit' zostanie nieuchronnie utracony dopiero po 30 dniach od wykonania polecenia *git gc*. Jeśli chcałbyś zapobiec automatyycznemu wykonywaniu *git gc*: $ git config gc.auto 0 wtedy 'commits' będą tylko wtedy usuwane, gdy ręcznie wykonasz polecenie *git gc*. === Budować na bazie Gita === W prawdziwym unixowym świecie sama konstrukcja Gita pozwala na wykorzystanie go jako funkcji niskiego poziomu przez inne aplikacje, jak na przykład interfejsy graficzne i aplikacje internetowe, alternatywne narzędzia konsoli, narzędzia patchujące, narzędzia pomocne w importowaniu i konwertowaniu i tak dalej. Nawek same polecenia Git są czasami malutkimi skryptami, jak krasnoludki na ramieniu olbrzyma. Przykładając trochę ręki możesz adoptować Git do twoich własnych potrzeb. Prostą sztuczką może być korzystanie z zintegrowanej w Git funkcji aliasu, by skrócić najczęściej stosowane polecenia: $ git config --global alias.co checkout $ git config --global --get-regexp alias # wyświetli aktualne aliasy alias.co checkout $ git co foo # to samo co 'git checkout foo' Czymś troszeczkę innym będzie zapis nazwy aktualnego 'branch' w prompcie lub jako nazwy okna. Polecenie: $ git symbolic-ref HEAD pokaże nazwę aktualnego 'branch'. W praktyce chciałbyś raczej usunąć "refs/heads/" i ignorować błędy: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- Podkatakog +contrib+ jest wielkim znaleziskiem narzędzi zbudowanych dla Gita. Z czasem niektóre z nich mogą uzyskać status oficjalnych poleceń. W dystrybucjach Debiana i Ubuntu znajdziemy ten katalog pod +/usr/share/doc/git-core/contrib+. Ulubionym przedstawicielem jest +workdir/git-new-workdir+. Poprzez sprytne przelinkowania skrypt ten tworzy nowy katalog roboczy, który dzieli swoją historię wersji z oryginalnym repozytorium: $ git-new-workdir istniejacy/repo nowy/katalog Ten nowy katalog i znajdujące się w nim pliki można sobie wyobrazić jako klon, z tą różnicą, że ze względu na wspólną niepodzieloną historie obje wersje pozostaną zsynchronizowane. Synchronizacja za pomocą 'merge', 'push', czy 'pull' nie będzie konieczna. === Śmiałe wyczyny === Obecnie Git dość dobrze chroni użytkownika przed przypadkowym zniszczeniem danych. Ale, jeśli wiemy co robić, możemy obejść środki ochrony najczęściej stosowanych poleceń. *Checkout*: nie wersjonowane zmiany doprowadzą do niepowodzenia polecenia 'checkout'. Aby mimo tego zniszczyć zmiany i przywołać istniejący 'commit', możemy skorzystać z opcji 'force': $ git checkout -f HEAD^ Jeśli poleceniu 'checkout' podamy inną ścieżkę, środki ochrony nie znajdą zastosowania. Podana ścieżka zostanie bez pytania zastąpiona. Bądź ostrożny stosując 'checkout' w ten sposób. *reset*: reset odmówi pracy, jeśli znajdzie niewersjonowane zmiany. By zmusić go do tego, możesz użyć: $ git reset --hard 1b6d *Branch*: Skasowanie 'branches' też się nie powiedzie, jeśli mogłyby przez to zostać utracone zmiany. By wymusić skasowanie, podaj: $ git branch -D martwy_branch # zamiast -d Również nie uda się próba przesunięcia 'branch' poleceniem 'move', jeśliby miałoby to oznaczać utratę danych. By wymusić przesunięcie, podaj: $ git branch -M źródło cel # zamiast -m Inaczej niż w przypadku 'checkout' i 'reset', te oba polecenia przesuną zniszczenie danych. Zmiany te zostaną zapisane w podkatalogu .git i mogą znów zostać przywrócone, jeśli znajdziemy odpowiedni hash SHA1 w `.git/logs` (zobacz "Łowcy głów" powyżej). Standardowo dane te pozostają jeszcze przez 2 tygodnie. *clean*: Różnorakie polecenia Git nie chcą zadziałać, ponieważ podejżewają konflikty z niewersjonowanymi danymi. Jeśli jesteś pewny, że wszystkie niezwersjonowane pliki i katalogi są zbędne, skasujesz je bezlitośnie poleceniem: $ git clean -f -d Następnym razem te uciążliwe polecenia zaczną znów być posłuszne. === Zapobiegaj złym 'commits' === Głupie błędy zaśmiecają moje repozytoria. Najbardziej fatalny jest brak plików z powodu zapomnianych *git add*. Mniejszymi usterkami mogą być spacje na końcu linii i nierozwiązane konflikty poleceń 'merge': mimo iż nie są groźne, życzyłbym sobie, by nigdy nie wystąpiły publicznie. Gdybym tylko zabezpieczył się wcześniej, stosując prosty _hook_, który alarmowałby mnie przy takich problemach. $ cd .git/hooks $ cp pre-commit.sample pre-commit # Starsze wersje Gita wymagają jeszcze: chmod +x pre-commit I już 'commit' przerywa, jeśli odkryje niepotrzebne spacje na końcu linii albo nierozwiązane konflikty 'merge'. Na początku *pre-commit* tego 'hook' umieściłbym dla ochrony przed rozdrobnieniem: if git ls-files -o | grep '\.txt$'; then echo FAIL! Untracked .txt files. exit 1 fi Wiele operacji Gita pozwala na używanie 'hooks'; zobacz też: *git help hooks*. We wcześniejszym rozdziale "Git poprzez http" przytoczyliśmy przykład 'hook' dla *post-update*, który wykonywany jest zawsze, jeśli znacznik 'HEAD' zostaje przesunięty. Ten przykładowy skrypt 'post-update' aktualizuje dane, które potrzebne są do komunikacji poprzez 'Git-agnostic transports', jak na przykład HTTP. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pl/history.txt��������������������������������������������������������������������0000644�0001750�0001750�00000031223�12666307504�015656� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Lekcja historii == Jedną z charakterystycznych cech rozproszonej natury Git jest to, że jego kronika historii może być łatwo edytowana. Ale jeśli masz zamiar manipulować przeszłością, bądź ostrożny: zmieniaj tylko tą część historii, którą wyłącznie jedynie ty sam posiadasz. Tak samo jak Narody ciągle dyskutują, który jakie popełnił okrucieństwa, popadniesz w kłopoty przy synchronizacji, jeśli ktoś inny posiada klon z różniącą się historią i jeśli te odgałęzienia mają się wymieniać. Niektórzy programiści zarzekają się w kwestii nienaruszalności historii - ze wszystkimi jej błędami i niedociągnięciami. Inni uważają, że odgałęzienia powinny dobrze się prezentować nim zostaną przedstawione publicznie. Git jest wyrozumiały dla obydwu stron. Tak samo jak 'clone', 'branch', czy 'merge', możliwość zmian kroniki historii to tylko kolejna mocna strona Gita. Stosowanie lub nie, tej możliwości zależy wyłącznie od ciebie. === Muszę się skorygować === Właśnie wykonałaś 'commit', ale chętnie chciałbyś podać inny opis? Wpisujesz: $ git commit --amend by zmienić ostatni opis. Zauważasz jednak, że zapomniałaś dodać jakiegoś pliku? Wykonaj *git add*, by go dodać a następnie poprzednią instrukcje. Chcesz wprowadzić jeszcze inne zmiany do ostatniego 'commit'? Wykonaj je i wpisz: $ git commit --amend -a === ... i jeszcze coś === Załóżmy, że poprzedni problem będzie 10 razy gorszy. Po dłuższej sesji zrobiłaś całą masę 'commits'. Nie jesteś jednak szczęśliwy z takiego zorganizowania, a niektóre z 'commits' mogłyby być inaczej sformułowane. Wpisujesz: $ git rebase -i HEAD~10 a ostatnie 10 'commits' pojawią się w preferowanym przez ciebie edytorze. Przykładowy wyciąg: pick 5c6eb73 Added repo.or.cz link pick a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile Starsze 'commits' poprzedzają młodsze, inaczej niż w poleceniu `log` Tutaj 5c6eb73 jest najstarszym 'commit', a 100834f najnowszym. By to zmienić: - Usuń 'commits' poprzez skasowanie linii. Podobnie jak polecenie 'revert', będzie to jednak wyglądało jakby wybrane 'commit' nigdy nie istniały. - Przeorganizuj 'commits' przesuwając linie. - Zamień `pick` na: * `edit` by zaznaczyć 'commit' do 'amend'. * `reword`, by zmienić opisy logu. * `squash` by połączyć 'commit' z poprzednim ('merge'). * `fixup` by połączyć 'commit' z poprzednim ('merge') i usunąć zapisy z logu. Na przykład chcemy zastąpić drugi `pick` na `squash`: Zapamiętaj i zakończ. Jeśli zaznaczyłaś jakiś 'commit' do 'edit', wpisz: $ git commit --amend pick 5c6eb73 Added repo.or.cz link squash a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile Po zapamiętaniu i wyjściu Git połączy a311a64 z 5c6eb73. Thus *squash* merges into the next commit up: think ``squash up''. Git połączy wiadomości logów i zaprezentuje je do edycji. Polecenie *fixup* pominie ten krok, wciśnięte logi zostaną pominięte. Jeśli zaznaczyłeś 'commit' opcją *edit*, Git przeniesie cię do najstarszego takiego 'commit'. Możesz użyć 'amend', jak opisane w poprzednim rozdziale, i utworzyć nowy 'commit' mający się tu znaleźć. Gdy już będziesz zadowolony z ``retcon'', przenieś się na przód w czasie: $ git rebase --continue Git powtarza 'commits' aż do następnego *edit* albo na przyszłość, jeśli żadne nie stoją na prożu. Możesz równierz zrezygnować z 'rebase': $ git rebase --abort A więc, stosuj polecenie 'commit' wcześnie i często: możesz później zawsze posprzątać za pomocą 'rebase'. === Lokalne zmiany na koniec === Pracujesz nad aktywnym projektem. Z biegiem czasu nagromadziło się wiele 'commits' i wtedy chcesz zsynchronizować za pomocą 'merge' z oficjalną gałęzią. Ten cykl powtarza się kilka razy zanim jesteś gotowy na 'push' do centralnego drzewa. Teraz jednak historia w twoim lokalnym klonie jest chaotycznym pomieszaniem twoich zmian i zmian z oficjalnego drzewa. Chciałbyś raczej widzieć twoje zmiany uporządkowane chronologicznie w jednej sekcji i za oficjalnymi zmianami. To zadanie dla *git rebase*, jak opisano powyżej. W wielu przypadkach możesz skorzystać z przełącznika *--onto* by zapobiec interakcji. Przeczytaj też *git help rebase* dla zapoznania sie z obszernymi przykładami tej zadziwiającej funkcji. Możesz również podzielić 'commits'. Możesz nawet przeorganizować 'branches' w repozytorium. Bądź ostrożny korzystając z 'rebase', to bardzo mocne polecenie. Zanim dokonasz skomplikowanych 'rebase', zrób backup za pomocą *git clone* === Przepisanie historii === Czasami potrzebny ci rodzaj systemu kontroli porównywalnego do wyretuszowania osób z oficjalnego zdjęcia, by w stalinowski sposób wymazać je z historii. Wyobraź sobie, że chcesz opublikować projekt, jednak zawiera on pewny plik, który z jakiegoś ważnego powodu musi pozostać utajniony. Być może zapisałem numer karty kredytowej w danej tekstowej i nieumyślnie dodałem do projektu? Skasowanie tej danej nie ma sensu, ponieważ poprzez starsze 'commits' można nadal ją przywołać. Musimy ten plik usunąć ze wszystkich 'commits': $ git filter-branch --tree-filter 'rm bardzo/tajny/plik' HEAD Sprawdź *git help filter-branch*, gdzie przykład ten został wytłumaczony i przytoczona została jeszcze szybsza metoda. Ogólnie poprzez *filter-branch* da się dokonać zmian w dużych zakresach historii poprzez tylko jedno polecenie. Po tej operacji katalog +.git/refs/original+ opisuje stan przed jej wykonaniem. Sprawdź czy 'filter-branch' zrobił to, co od niego oczekiwałaś, następnie skasuj ten katalog zanim wykonasz następne polecenia 'filter-branch'. Wreszcie zamień wszystkie klony twojego projektu na zaktualizowaną wersję, jeśli masz zamiar prowadzić z nimi wymianę. === Tworzenie historii === [[makinghistory]] Masz zamiar przenieść projekt do Gita? Jeśli twój projekt był dotychczas zarządzany jednym z bardziej znanych systemów, to istnieje duże prawdopodobieństwo, że ktoś napisał już odpowiedni skrypt, który umożliwi ci eksportowanie do Gita całej historii. W innym razie przyjrzyj się funkcji *git fast-import*, która wczytuje tekst w specjalnym formacie by następnie odtworzyć całą historię od początku. Często taki skrypt pisany jest pośpiesznie i służy do jednorazowego wykorzystania, aby tylko w jednym przebiegu udała się migracja projektu. Utwórz na przykład z następującej listy tymczasowy plik, na przykład jako: `/tmp/history`: ---------------------------------- commit refs/heads/master committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000 data <<EOT Initial commit. EOT M 100644 inline hello.c data <<EOT #include <stdio.h> int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800 data <<EOT Replace printf() with write(). EOT M 100644 inline hello.c data <<EOT #include <unistd.h> int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- Następnie utwórz repozytorium Git z tymczasowego pliku poprzez wpisanie: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history Aktualną wersję projektu możesz przywołać poprzez: $ git checkout master Polecenie *git fast-export* konwertuje każde repozytorium do formatu *git fast-import*, możesz przestudiować komunikaty tego polecenia, jeśli masz zamiar napisać programy eksportujące a oprócz tego, by przekazywać repozytoria jako czytelne dla ludzi zwykłe pliki tekstowe. To polecenie potrafi przekazywać repozytoria za pomocą zwykłego pliku tekstowego. === Gdzie wszystko się zepsuło? === Właśnie znalazłaś w swoim programie funkcję, która już nie chce działać, a jesteś pewna, że czyniła to jeszcze kilka miesięcy temu. Ach! Skąd wziął się ten błąd? A gdybym tylko lepiej przetestowała ją wcześniej, zanim weszła do wersji produkcyjnej. Na to jest już za późno. Jakby nie było, pod warunkiem, że często używałaś 'commit', Git może ci zdradzić gdzie szukać problemu. $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git przywoła stan, który leży dokładnie pośrodku. Przetestuj funkcję, a jeśli ciągle jeszcze nie działa: $ git bisect bad Jeśli nie, zamień "bad" na "good". Git przeniesie cię znowu do stanu dokładnie pomiędzy znanymi wersjami "good" i "bad", redukując w ten sposób możliwości. Po kilku iteracjach doprowadzą cię te poszukiwania do 'commit', który jest odpowiedzialny za kłopoty. Po skończeniu dochodzenia przejdź do oryginalnego stanu: $ git bisect reset Zamiast sprawdzania zmian ręcznie, możesz zautomatyzować poszukiwania za pomocą skryptu: $ git bisect run mój_skrypt Git korzysta tutaj z wartości zwróconej przez skrypt, by ocenić czy zmiana jest dobra ('good'), czy zła ('bad'): Skrypt powinien zwracać 0 dla 'good', 128, jeśli zmiana powinna być pominięta, i coś pomiędzy 1 - 127 dla 'bad'. Jeśli wartość zwrócona jest ujemna, program 'bisect' przerywa pracę. Możesz robić jeszcze dużo innych rzeczy: w pomocy znajdziesz opis w jaki sposób wizualizować działania 'bisect', sprawdzić czy powtórzyć log bisect, wyeliminować nieistotne zmiany dla zwiększenia prędkości poszukiwań. === Kto ponosi odpowiedzialność? === Jak i wiele innych systemów kontroli wersji również i Git posiada polecenie 'blame': $ git blame bug.c które komentuje każdą linię podanego pliku, by pokazać kto ją ostatnio zmieniał i kiedy. W przeciwieństwie do wielu innych systemów, funkcja ta działa offline, czytając tylko z lokalnego dysku. === Osobiste doświadczenia === W scentralizowanym systemie kontroli wersji praca nad kroniką historii jest skomplikowanym zadaniem i zarezerwowanym głównie dla administratorów. Polecenia 'clone', 'branch' czy 'merge' nie są możliwe bez podłączenia do sieci. Również takie podstawowe funkcje, jak przeszukanie historii czy 'commit' jakiejś zmiany. W niektórych systemach użytkownik potrzebuje działającej sieci nawet by zobaczyć dokonane przez siebie zmiany, albo by w ogóle otworzyć plik do edycji. Scentralizowane systemy wykluczają pracę offline i wymagają drogiej infrastruktury sieciowej, w szczególności gdy wzrasta liczba programistów. Najgorsze jednak, iż z czasem wszystkie operacje stają się wolniejsze, z reguły do osiągnięcia punktu, gdzie użytkownicy unikają zaawansowanych poleceń, aż staną się one absolutnie konieczne. W ekstremalnych przypadkach dotyczy to również poleceń podstawowych. Jeśli użytkownicy są zmuszeni do wykonywania powolnych poleceń, produktywność spada, ponieważ ciągle przerywany zostaje tok pracy. Dowiedziałem się o tym fenomenie na sobie samym. Git był pierwszym systemem kontroli wersji którego używałem. Szybko dorosłem do tej aplikacji i przyjąłem wiele funkcji za oczywiste. Wychodziłem też z założenia, że inne systemy są podobne: wybór systemu kontroli wersji nie powinien zbyt bardzo odbiegać od wyboru edytora tekstu, czy przeglądarki internetowej. Byłem zszokowany, gdy musiałem później korzystać ze scentralizowanego systemu. Niesolidne połączenie internetowe ma niezbyt duży wpływ na Gita, praca staje się jednak prawie nie możliwa, gdy wymagana jest niezawodność porównywalny z lokalnym dyskiem. Poza tym sam łapałem się na tym, że unikałem pewnych poleceń i związanym z nimi czasem oczekiwania, w sumie wszystko to wpływało mocno na wypracowany przeze mnie system pracy. Gdy musiałem wykonywać powolne polecenia, z powodu ciągłego przerywanie toku myślenia, powodowałem nieporównywalne szkody dla całego przebiegu pracy. Podczas oczekiwania na zakończenie komunikacji pomiędzy serwerami dla przeczekania zaczynałem robić coś innego, na przykład czytałem maile albo pisałem dokumentację. Gdy wracałem do poprzedniego zajęcia, po zakończeniu komunikacji, dawno straciłem wątek i traciłem czas, by znów przypomnieć sobie co właściwie miałem zamiar zrobić. Ludzie nie potrafią dobrze dostosować się do częstej zmiany kontekstu. Był też taki ciekawy efekt http://pl.wikipedia.org/wiki/Tragedia_wspólnego_pastwiska[tragedii wspólnego pastwiska]: przypominający przeciążenia w sieci - pojedyncze indywidua pochłaniają więcej pojemności sieci niż to konieczne, by uchronić się przed mogącymi ewentualnie wystąpić w przyszłości niedoborami. Suma tych starań pogarsza tylko przeciążenia, co motywuje jednostki do zużywania jeszcze większych zasobów, by ochronić się przed jeszcze dłuższymi czasami oczekiwania. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pl/translate.txt������������������������������������������������������������������0000644�0001750�0001750�00000002607�12666307504�016156� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Załącznik B: Przetłumaczyć to HOWTO == Aby przetłumaczyć moje HOWTO polecam wykonanie następujących poniżej kroków, wtedy moje skrypty będą w prosty sposób mogły wygenerować wersje HTML i PDF. Poza tym wszystkie tłumaszenia mogą być prowadzone w jednym repozytorium. Sklonuj texty źródłowe, następnie utwórz katalog o nazwie skrótu IETF przetłumaszonego języka: sprawdź http://www.w3.org/International/articles/language-tags/Overview.en.php[Artykół W3C o internacjonalizacji]. Na przykład, angielski to "en", a japoński to "ja". Skopiuj wszystkie pliki +txt+ z katalogu "en" do nowoutworzonego katalogu. Aby przykładowo przetłumaczyć to HOWTO na http://de.wikipedia.org/wiki/Klingonische_Sprache[Klingonisch], musisz wykonać następujące polecenia: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" jest skrótem IETF języka Klingonisch. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Przetłumacz ten plik. i zrób to z każdą następną daną textową. Edytuj Makefile i dodaj skrót języka do zmiennej `TRANSLATIONS`. Teraz możesz swoją pracę w każdej chwili sprawdzić: $ make tlh $ firefox book-tlh/index.html Używaj często 'commit' a gdy już skończysz, to daj znać. GitHub posiada interfejs, który to ułatwi: utwórz twój własny 'Fork' projektu "gitmagic", 'push' twoje zmiany i daj mi znać, by je 'mergen'. �������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pl/intro.txt����������������������������������������������������������������������0000644�0001750�0001750�00000017355�12666307504�015322� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Wprowadzenie == By wprowadzić w zagadnienie kontroli wersji, posłużę się pewną analogią. Dla bardziej rozsądnego wyjaśnienia przeczytajcie http://pl.wikipedia.org/wiki/System_kontroli_wersji[Artykuł Wikipedii] na ten temat. === Praca jest zabawą === Gram w gry komputerowe chyba już przez całe moje życie. W przeciwieństwie do tego, systemy kontroli wersji zacząłem stosować dopiero jako dorosły. Przypuszczam, że nie jestem tu odosobniony, a porównanie to pomoże mi w prosty sposób wytłumaczyć jego idee. Wyobraź sobie pracę nad twoim kodem albo edycję dokumentu jak granie na komputerze. Jeśli dobrze ci poszło, chcesz zabezpieczyć swoje osiągnięcia. W tym celu klikasz na 'zapisz' w wybranym edytorze. Niestety wymaże to poprzednio zapamiętaną wersję. To jak w grach starej szkoły, które posiadały pamięć na zapisanie tylko jednego stanu: oczywiście, mogłaś zapamiętać, ale już nigdy nie mogłaś powrócić do poprzednio zapisanej wersji. To była hańba, bo być może poprzednio zabezpieczony stan znajdował się w jakimś bardzo interesującym miejscu gry, do którego chętnie chciałbyś jeszcze kiedyś wrócić. Albo jeszcze gorzej, twój zabezpieczony stan utknął w niemożliwym do dokończenia gry miejscu i musisz zacząć wszystko od początku. === Kontrola wersji === Podczas edytowania dokumentu, by uchronić starą wersję, możesz poprzez wybranie 'zapisz jako ...' zapisać twój dokument pod inną nazwą lub zapamiętać w innym miejscu. Poza tym możesz go jeszcze spakować, by zaoszczędzić miejsce na dysku. Jest to prymitywna i pracochłonna forma kontroli wersji. Gry komputerowe robią tak już od długiego czasu, wiele z nich posiada tak automatycznie utworzone punkty opatrzone sygnaturą czasu. Skomplikujmy teraz trochę cały ten problem. Powiedzmy, że posiadasz całą masę plików, które w jakiś sposób są ze sobą powiązane, na przykład kod źródłowy jakiegoś projektu lub pliki strony internetowej. Jeśli chcesz otrzymać starszą wersję musisz archiwizować cały katalog. Archiwizowanie w ten sposób wielu wersji jest pracochłonne i szybko może stać się kosztowne, zabierając niepotrzebnie miejsce na dysku. Niektóre gry komputerowe składały się rzeczywiście z jednego katalogu pełnego plików. Gry ukrywały szczegóły przed graczem i prezentowały wygodny interfejs, do zarządzania różnymi wersjami katalogu. Systemy kontroli wersji nie różnią się tutaj zbytnio. Wszystkie posiadają wygodne interfejsy, umożliwiającymi zarządzanie katalogami pełnymi plików. Możesz archiwizować stan katalogu tak często jak często zechcesz i później możesz do każdego z tych punktów powrócić. W przeciwieństwie jednak do gier, są one z reguły wszystkie zoptymalizowane pod kątem oszczędności pamięci. W większości przypadków tylko niewiele danych ulega zmianie pomiędzy dwoma wersjami, a same zmiany nie są zbyt obszerne. Oszczędność miejsca na dysku polega głównie na zapamiętywaniu jedynie różnic, a nie kopii całego katalogu. === Kontrola rozproszona === Wyobraź sobie teraz bardzo trudną grę komputerową. Tak trudną, że wielu doświadczonych graczy na całym świecie postanawia o wspólnych siłach przejść grę, wymieniając się w tym celu swoimi wynikami. 'Speedruns' mogą posłużyć jako przykład z prawdziwego życia: gracze, którzy wyspecjalizowali się w różnych poziomach gry współpracują ze sobą dla uzyskania fascynujących wyników. W jaki sposób skonstruowałbyś taki system, który w prosty sposób byłby w stanie udostępnić osiągnięcia innych? I dodawał nowe? Kiedyś każdy projekt korzystał z własnego scentralizowanego systemu kontroli wersji. Jeden serwer zapamiętywał wszystkie gry, nikt inny. Każdy gracz posiadał jedynie kilka zapamiętanych na swoim komputerze gier. Jeśli jakiś gracz chciał popchać grę trochę do przodu, musiał najpierw zładować z serwera jej aktualny stan, trochę pograć, zapisać własny stan, a następnie załadować na serwer, by mógł go wykorzystać ktoś inny. A gdy jakiś gracz z jakiegoś powodu chce otrzymać jakiś starszy stan? Może aktualnie zapamiętany stan gry nie jest do przejścia, bo ktoś na trzecim poziomie zapomniał zabrać jakiś obiekt, no i teraz próbują znaleźć stan od którego startując gra znowu stanie się możliwa do przejścia. Albo chcą porównać dwa stany, by sprawdzić ile któryś gracz włożył pracy. Istnieje wiele powodów, dla których można chcieć zobaczyć straszą wersję, rezultat jednak jest zawsze taki sam. Za każdym razem trzeba ściągnąć wszystkie dane z serwera. Czym więcej gier zostało zapamiętanych, tym więcej wymaga to komunikacji. Nową generację systemów kontroli wersji, do których zalicza się również Git, nazywa się systemami rozproszonymi, mogą być one rozumiane jako uogólnienie systemów scentralizowanych. Jeśli gracze ładują teraz z serwera, otrzymują każdy zapisany stan, a nie tylko zapisany jako ostatni. Wygląda to jak tworzenie kopii lustrzanej serwera. Stworzenie pierwszego klonu może wydać się drogie, przede wszystkim, jeśli projekt posiada długą historię, ale na dłuższy okres to się opłaci. Jedną z bezpośrednich zalet jest to, że kiedykolwiek potrzebny będzie nam jakiś starszy stan, komunikacja z głównym serwerem będzie zbędna. === Głupi przesąd === Szeroko rozpowszechnianym nieporozumieniem jest opinia, że rozproszony system nie nadaje się dla projektów wymagających oficjalnego centralnego repozytorium. Nic bardziej mylnego. Fotografując kogoś nie kradniemy od razu jego duszy. Tym samym klonowanie centralnego repozytorium nie umniejsza jego znaczenia. Jednym z pierwszych pozytywnych skutków jest to, iż wszystko co potrafi scentralizowany system kontroli wersji, dobrze skonstruowany system rozproszony potrafi lepiej. Zasoby sieciowe są po prostu droższe niż zasoby lokalne. Nawet jeśli w późniejszym czasie dostrzeżemy pewne niedociągnięcia systemów rozproszonych, można powyższe przyjąć jako ogólną zasadę, unikając niestosownych porównań. Mały projekt wykorzysta prawdopodobnie tylko ułamek możliwości systemu. Ale, by od razu z tego powodu korzystać z prostszego systemu, nie posiadającego możliwości późniejszej rozbudowy, to tak jak stosowanie rzymskich cyfr do przeprowadzania obliczeń na małych liczbach. Ponadto możliwe, że twój projekt przerośnie początkowe oczekiwania. Używanie Gita od samego początku, to jak noszenie ze sobą szwajcarskiego scyzoryka, nawet gdy najczęściej służy do otwierania butelek. Być może pewnego dnia będziesz pilnie potrzebowała użyć śrubokręt, ucieszysz się, że masz przy sobie coś więcej niż tylko zwykły otwieracz. === Kolizje przy scalaniu === Do przedstawienia tego tematu wykorzystanie analogii do gier komputerowych byłoby naciągane. Wyobraźmy sobie znowu, że edytujemy dokument. Alicja dodaje linijkę na początku dokumentu, natomiast Bob linijkę na jego końcu. Obydwoje ładują swoje zmiany na serwer. Większość systemów automatycznie wybierze rozsądną drogę: zaakceptuje obie zmiany i połączy je ze sobą, tym samym obje poprawki wpłyną do dokumentu. Wyobraź sobie jednak, że Alicja i Bob dokonują zmian w tej samej linijce. W tym wypadku dalsza praca nie będzie możliwa bez ludzkiego udziału. Druga z osób, próbująca zładować dokument na serwer, zostanie poinformowana o wystąpieniu konfliktu podczas łączenia ('merge') i musi zadecydować, którą ze zmian przyjąć, ewentualnie ponownie zrewidować całą linijkę. Mogą wystąpić dużo bardziej skomplikowane sytuacje. Systemy kontroli wersji potrafią poradzić sobie z prostymi przypadkami a te trudniejsze pozostawiają ludziom. Zazwyczaj sposób ich zachowania można skonfigurować. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pl/clone.txt����������������������������������������������������������������������0000644�0001750�0001750�00000030173�12666307504�015260� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Klonowanie == W starszych systemach kontroli wersji polecenie 'checkout' stanowi standardową operacje pozyskiwania danych. Otrzymasz nią zbiór plików konkretnej wersji. W Git i innych rozproszonych systemach standardowo służy temu operacja 'clone'. By pozyskać dane, tworzysz najpierw klon całego repozytorium. Lub inaczej mówiąc, otrzymujesz lustrzane odbicie serwera. Wszystko, co można zrobić w centralnym repozytorium, możesz również robić z klonem. === Synchronizacja komputera === W celu ochrony danych mógłbym jeszcze zaakceptować korzystanie z archiwum 'tar', a dla prostej synchronizacji używania *rsync*. Jednak czasami pracując na laptopie, a innym razem na komputerze stacjonarnym, może się zdarzyć, że komputery nie miały możliwości zsynchwonizowania się w międzyczasie. Utwórz repozytorium Gita w wykonaj 'commit' twoich danych na jednym z komputerów. Potem na następnym wpisz: $ git clone drugi.komputer:/ścieżka/do/danych by otrzymać drugą kopie danych i jednocześnie utworzyć repozytorium Gita na drugim komputerze. Od teraz poleceniem: $ git commit -a $ git pull drugi.komputer:/ścieżka/do/danych HEAD przenosisz stan drugiego komputera na komputer na którym właśnie pracujesz. Jeśli dokonałaś zmian w tym samym pliku na obydwu komputerach, Git poinformuje cię o tym, po usunięciu konfliktu powinnaś ponowić 'commit'. === Klasyczna kontrola kodu źródłowego === Utwórz repozytorium Gita w katalogu roboczym projektu: $ git init $ git add . $ git commit -m "Pierwszy commit" Na centralnym serwerze utwórz gołe ('bare') repozytorium w jakimkolwiek katalogu: $ mkdir proj.git $ cd proj.git $ git --bare init $ touch proj.git/git-daemon-export-ok W razie konieczności, wystartuj daemon Gita: $ git daemon --detach # ponieważ, gdyby uruchomiony był wcześniej Jeśli korzystasz z hostingu to poszukaj na stronie usługodawcy wskazówek jak utworzyć repozytorium 'bare'. Zwykle konieczne jest do tego wypełnienie formularza online na jego stronie. Popchaj ('push') twój projekt teraz na centralny serwer: $ git push centralny.serwer/ścieżka/do/projektu.git HEAD By pozyskać kod źródłowy programista podaje zwykle polecenie w rodzaju: $ git clone główny.serwer/ścieżka/do/projektu.git Po dokonaniu edycji programista zapamiętuje zmiany najpierw lokalnie: $ git commit -a Aby zaktualizować do wersji istniejącej na głównym serwerze: $ git pull Jeśli wystąpią jakiekolwiek konflikty łączenia ('merge') , powinny być najpierw usunięte i na nowo zostać wykonany 'commit'. $ git commit -a Lokalne zmiany przekazujemy do serwera poleceniem: $ git push Jeśli w międzyczasie nastąpiły nowe zmiany na serwerze wprowadzone przez innego programistę, twój 'push' nie powiedzie się. Zaktualizuj lokalne repozytorium ponownie poleceniem 'pull', pozbądź się konfliktów i spróbuj jeszcze raz. Programiści potrzebują dostępu poprzez SSH dla wykonania poleceń 'pull' i 'push'. Mimo to, każdy może skopiowć kod źródłowy poprzez podanie: $ git clone git://główny.serwer/ścieżka/do/projektu.git Protokół GIT przypomina HTTP: nie posiada autentyfikacji, a więc każdy może skopiować dane. Przy ustawieniach standardowych wykonanie 'push' za pomocą protokołu GIT jest zdezaktywowane. === Utajnienie źródła === Przy projektach Closed-Source wyklucz używanie poleceń takich jak 'clone' i upewnij się, że nie został utworzony plik o nazwie 'git-daemon-export-ok'. Wtedy repozytorium nie może już komunikować się poprzez protokół 'git', tylko posiadający dostęp przez SHH mogą widzieć dane. Jeśli wszystkie repozytoria są zamknięte, nie ma potrzeby startować daemona 'git', ponieważ cała komunikacja odbywa się wyłącznie za pomocą SSH. === Gołe repozytoria === Określenie: 'gołe' ('bare') repozytorium, powstało, ze względu na fakt, iż repozytorium to nie posiada katalogu roboczego. Posiada jedynie dane, które są zwykle schowane w podkatalogu '.git'. Innymi słowy: zarządza historią projektu, nie posiada jednak katalogu roboczego jakiejkolwiek wersji. Repozytorium 'bare' przejmuje rolę podobną do roli głównego serwera w scentralizowanych systemach kontroli wersji: dach twojego projektu. Programiści klonują twój projekt stamtąd i przesyłają tam ostatnie oficjalne zmiany. Często znajduje się ono na serwerze, którego jedynym zadaniem jest wyłącznie dystrybucja danych. Sama praca nad projektem przebiega na jego klonach, w ten sposób główne repozytorium daje sobie radę nie korzystając z katalogu roboczego. Wiele z poleceń Gita nie będzie funkcjonować w repozytoriach 'bare', chyba że ustawimy zmienną systemową GIT_DIR na katalog roboczy repozytorium albo przekażemy opcję `--bare`. === 'Push', czy 'pull'? === Dlaczego wprowadziliśmy polecenie 'push', a nie pozostaliśmy przy znanym nam już 'pull'? Po pierwsze, 'pull' nie działa z repozytoriami 'bare': zamiast niego używaj 'fetch', polecenia którym zajmiemy się później. Również gdybyśmy nawet używali normalnego repozytorium na serwerze centralnym, polecenie 'pull' byłoby raczej niewygodne. Musielibyśmy wpierw zalogować się na serwerze i przekazać poleceniu 'pull' adres IP komputera z którego chcemy ściągnąć pliki. Jeśli w ogóle posiadalibyśmy dostęp do konsoli serwera, to prawdopodobnie przeszkodziłaby nam firewalle na drodze do naszego komputera. W każdym bądź razie, nawet jeśli nawet mogło by to zadziałać, odradzam z korzystania z funkcji 'push' w ten sposób. Poprzez samo posiadanie katalogu roboczego na serwerze mogłoby powstać wiele nieścisłości. Krótko mówiąc, podczas gdy uczysz się korzystania z Git, korzystaj z polecenia 'push' tylko, gdy celem jest repozytorium 'bare', w wszystkich innych wypadkach z 'pull'. === Rozwidlenie projektu === Jeśli nie potrafisz już patrzeć na kierunek rozwoju w jakim poszedł jakiś projekt. Uważasz, że potrafisz to lepiej. To po prostu utwórz jego 'fork', w tym celu na twoim serwerze podaj: $ git clone git://główny.serwer/ścieżka/do/danych Następnie, poinformuj wszystkich o nowym 'forku' projektu na twoim serwerze. W każdej późniejszej chwili możesz dokonać łączenia ('merge') zmian z oryginalnego projektu, poprzez: $ git pull === Ultymatywny backup === Chcesz posiadać liczne, wolne od manipulacji, redundantne kopie bezpieczeństwa w różnych miejscach? Jeśli projekt posiada wielu programistów, nie musisz niczego robić. Ponieważ każdy klon twojego kodu jest pełnowartościową kopią bezpieczeństwa. Nie tylko jego aktualna wersja, lecz również cała historia projektu. Gdy jakikolwiek klon zostanie uszkodzony, dzięki kryptograficznemu hashowaniu, zostanie to natychmiast rozpoznane, jeśli tylko dana osoba będzie próbować wymiany z innymi. Jeśli twój projekt nie jest jeszcze wystarczająco znany, spróbuj pozyskać tak wiele serwerów, ile to możliwe, by umieścić tam jego klon. Ci najbardziej paranoidalni powinni zawsze zapisywać 20 bajtów ostatniej sumy kontrolnej SHA1 dla HEAD i przechowywać w bezpiecznym miejscu. Musi być bezpieczny, jednak nie tajny. Na przykład opublikowanie go w gazecie dobrze by spełniło swoje zadanie, dość trudnym zadaniem byłoby zmanipulowanie każdej kopii gazety. === Wielozadaniowość z prędkością światła === Załóżmy, że chcesz pracować nad kilkoma funkcjami równocześnie. Wykonaj 'commit' i wpisz: $ git clone . /jakiś/nowy/katalog Za sprawą http://en.wikipedia.org/wiki/Hard_link[twardych linków] stworzenie lokalnej kopii zajmuje dużo mniej czasu i pamięci niż zwykły backup. Możesz pracować nad dwoma niezależnymi funkcjami jednocześnie. Na przykład, możesz pracować nad jednym klonem dalej, podczas gdy drugi jest właśnie kompilowany. W każdym momencie możesz wykonać 'commit' i 'pull' innego klonu. $ git pull /inny/klon HEAD === Kontrola wersji z podziemia === Pracujesz nad projektem, który używa innego systemu kontroli wersji i tęsknisz za Gitem? Utwórz po prostu repozytorium Gita w twoim katalogu roboczym: $ git init $ git add . $ git commit -m "Pierwszy commit" następnie sklonuj go: $ git clone . /jakiś/inny/katalog Przejdź teraz do nowego katalogu i pracuj według upodobania. Kiedyś zechcesz zsynchronizować pracę, idź do oryginalnego katalogu, zaktualizuj go najpierw z tym innym systemem kontroli wersji, następnie wpisz: $ git add . $ git add . $ git commit -m"Synchronizacja z innym systemem kontroli wersji" Teraz przejdź do nowego katalogu i podaj: $ git commit -a -m "Opis zmian" $ git pull Sposób w jaki przekażesz zmiany drugiemu systemowi zależy już od jego sposobu działania. Twój nowy katalog posiada dane ze zmianami przez ciebie wprowadzonymi. Wykonaj jeszcze adekwatne kroki żądane przez ten inny system kontroli wersji, by przekazać dane do centralnego repozytorium. Subversion, być może najlepszy z centralnych systemów, stosowany jest w wielu projektach. Komenda *git svn* automatyzuje powyższe kroki, może być użyta na przykład do http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[exportu projektu Git do repozytorium Subversion]. === Mercurial === Mercurial to podobny do Gita system kontroli wersji, który prawie bezproblemowo potrafi pracować z Gitem. Korzystając z rozszerzenia `hg-git` użytkownik Mercurial jest w stanie bez strat wykonywać 'push' i 'pull' z repozytorium Gita. Możesz ściągnąć sobie rozszerzenie `hg-git` za pomocą Gita: $ git clone git://github.com/schacon/hg-git.git albo za pomocą Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ Niestety nie są mi znane takie rozszerzenia dla Gita. Dlatego jestem za używaniem Gita jako głównego repozytorium, nawet gdy preferujesz Mercurial. W projektach prowadzonych za pomocą Mercurial często znajdziemy woluntariusza, który równolegle prowadzi repozytorium Gita, dzięki pomocy rozszerzenia `hg-git` projekty Gita automatycznie osiągają użytkowników Mercurial. To rozszerzenie potrafi również zmienić skład Mercurial w skład Gita, za pomocą komendy 'push' do gołego repozytorium Gita. Jeszcze łatwiej dokonamy tego skryptem `hg-fast-export.sh`, który możemy tu znaleźć: $ git clone git://repo.or.cz/fast-export.git Aby skonwertować, wejdź do pustego katalogu: $ git init $ hg-fast-export.sh -r /hg/repo po uprzednim dodaniu skryptu do twojego `$ PATH`. === Bazaar === Wspomnijmy również pokrótce o Bazaar, ponieważ jest to najbardziej popularny darmowy rozproszony system kontroli wersji po Git i Mercurial. Bazar będąc stosunkowo młodym systemem, posiada zaletę perspektywy czasu, jego twórcy mogli uczyć się na błędach z przeszłości i uniknąć historycznych naleciałości. Poza tym programiści byli świadomi popularności i wagi interakcji z innymi systemami kontroli wersji. Rozszerzenie `Bzr-git` pozwala użytkownikom Bazar dość łatwo pracować z repozytoriami Gita. Program `tailor` konwertuje składy Bazaar do składów Gita i może robić to na bieżąco, podczas gdy `bzr-fast-export` lepiej nadaje się do jednorazowej konwersji. === Dlaczego korzystam z Gita === Zdecydowałem się pierwotnie do wyboru Gita, ponieważ słyszałem, że jest w stanie zarządzać tak zawiłym i rozległym projektem jak kod źródłowy Linuksa. Jak na razie nie miałem powodów do zmiany. Git służył mi znakomicie i do tej pory mnie nie zawiódł. Ponieważ w pierwszej linii pracuję na Linuksie, problemy innych platform nie mają dla mnie znaczenia. Preferuję również programy 'C' i skrypty 'bash' w opozycji do na przykład Pythona: posiadają mniej zależności, wolę też, gdy kod jest wykonywany szybko. Myślałem już też nad tym, jak można by ulepszyć Gita, poszło to nawet tak daleko, że napisałem własną aplikacje podobną do niego, w celu jednak wyłącznie ćwiczeń akademickich. Nawet gdybym zakończył mój projekt, mimo to pozostałbym przy Git, bo ulepszenia byłyby zbyt minimalne by uzasadnić zastosowanie odosobnionego systemu. Oczywiście może się okazać, że twoje potrzeby i oczekiwania są zupełnie inne i być może wygodniej jest tobie z zupełnie innym systemem. Jakby jednak nie spojrzeć, stosując Git nie popełnisz tu niczego złego. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/�������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12666307504�013411� 5����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/drawbacks.txt������������������������������������������������������������������0000644�0001750�0001750�00000015353�12666307504�016122� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Appendix A: Git Shortcomings == There are some Git issues I've swept under the carpet. Some can be handled easily with scripts and hooks, some require reorganizing or redefining the project, and for the few remaining annoyances, one will just have to wait. Or better yet, pitch in and help! === SHA1 Weaknesses === As time passes, cryptographers discover more and more SHA1 weaknesses. Already, finding hash collisions is feasible for well-funded organizations. Within years, perhaps even a typical PC will have enough computing power to silently corrupt a Git repository. Hopefully Git will migrate to a better hash function before further research destroys SHA1. === Microsoft Windows === Git on Microsoft Windows can be cumbersome: - http://cygwin.com/[Cygwin], a Linux-like environment for Windows, contains http://cygwin.com/packages/git/[a Windows port of Git]. - http://code.google.com/p/msysgit/[Git on MSys] is an alternative requiring minimal runtime support, though a few of the commands need some work. === Unrelated Files === If your project is very large and contains many unrelated files that are constantly being changed, Git may be disadvantaged more than other systems because single files are not tracked. Git tracks changes to the whole project, which is usually beneficial. A solution is to break up your project into pieces, each consisting of related files. Use *git submodule* if you still want to keep everything in a single repository. === Who's Editing What? === Some version control systems force you to explicitly mark a file in some way before editing. While this is especially annoying when this involves talking to a central server, it does have two benefits: 1. Diffs are quick because only the marked files need be examined. 2. One can discover who else is working on the file by asking the central server who has marked it for editing. With appropriate scripting, you can achieve the same with Git. This requires cooperation from the programmer, who should execute particular scripts when editing a file. === File History === Since Git records project-wide changes, reconstructing the history of a single file requires more work than in version control systems that track individual files. The penalty is typically slight, and well worth having as other operations are incredibly efficient. For example, `git checkout` is faster than `cp -a`, and project-wide deltas compress better than collections of file-based deltas. === Initial Clone === Creating a clone is more expensive than checking out code in other version control systems when there is a lengthy history. 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. === Volatile Projects === Git was written to be fast with respect to the size of the changes. Humans make small edits from version to version. A one-liner bugfix here, a new feature there, emended comments, and so forth. But if your files are radically different in successive revisions, then on each commit, your history necessarily grows by the size of your whole project. There is nothing any version control system can do about this, but standard Git users will suffer more since normally histories are cloned. The reasons why the changes are so great should be examined. Perhaps file formats should be changed. Minor edits should only cause minor changes to at most a few files. Or perhaps a database or backup/archival solution is what is actually being sought, not a version control system. For example, version control may be ill-suited for managing photos periodically taken from a webcam. If the files really must be constantly morphing and they really must be versioned, a possibility is to use Git in a centralized fashion. One can create shallow clones, which checks out little or no history of the project. Of course, many Git tools will be unavailable, and fixes must be submitted as patches. This is probably fine as it's unclear why anyone would want the history of wildly unstable files. Another example is a project depending on firmware, which takes the form of a huge binary file. The history of the firmware is uninteresting to users, and updates compress poorly, so firmware revisions would unnecessarily blow up the size of the repository. In this case, the source code should be stored in a Git repository, and the binary file should be kept separately. To make life easier, one could distribute a script that uses Git to clone the code, and rsync or a Git shallow clone for the firmware. === Global Counter === Some centralized version control systems maintain a positive integer that increases when a new commit is accepted. Git refers to changes by their hash, which is better in many circumstances. But some people like having this integer around. Luckily, it's easy to write scripts so that with every update, the central Git repository increments an integer, perhaps in a tag, and associates it with the hash of the latest commit. Every clone could maintain such a counter, but this would probably be useless, since only the central repository and its counter matters to everyone. === Empty Subdirectories === Empty subdirectories cannot be tracked. Create dummy files to work around this problem. The current implementation of Git, rather than its design, is to blame for this drawback. With luck, once Git gains more traction, more users will clamour for this feature and it will be implemented. === Initial Commit === A stereotypical computer scientist counts from 0, rather than 1. Unfortunately, with respect to commits, git does not adhere to this convention. Many commands are unfriendly before the initial commit. Additionally, some corner cases must be handled specially, such as rebasing a branch with a different initial commit. Git would benefit from defining the zero commit: as soon as a repository is constructed, HEAD would be set to the string consisting of 20 zero bytes. This special commit represents an empty tree, with no parent, at some time predating all Git repositories. Then running git log, for example, would inform the user that no commits have been made yet, instead of exiting with a fatal error. Similarly for other tools. Every initial commit is implicitly a descendant of this zero commit. However there are some problem cases unfortunately. If several branches with different initial commits are merged together, then rebasing the result requires substantial manual intervention. === Interface Quirks === For commits A and B, the meaning of the expressions "A..B" and "A...B" depends on whether the command expects two endpoints or a range. See *git help diff* and *git help rev-parse*. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/branch.txt���������������������������������������������������������������������0000644�0001750�0001750�00000040055�12666307504�015413� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== 나뭇가지 (branch) 마법 == Git의 죽이는 기능들 중에는 즉석으로 브랜칭 및 병합이 가능하다는 것입니다. *예시문제*: 외부적인 요소들은 불가피하게 당신이 하던 일은 그만두게 합니다. 예를 들어, 치명적인 버그가 이미 배포된 버전에서 경고없이 퍼저나가게 생겼습니다. 프로그램에 새로 넣어야 할 기능이 있는데 데드라인은 가까워져 옵니다. 당신이 도움을 요청하고자 했던 개발자는 퇴사할려고 하니 도움을 요청할 수도 없고요. 시간이 촉박한 만큼 하던 일을 멈추고 버그를 고치는 데에 올인을 해야겠지요. 위와 같이 하던 일을 멈추는 것은 일의 생산성을 치명적으로 떨어트립니다. 특히나 지금까지 하던 일과 정 상관없는 부분의 프로그램을 건들어야 할 때 말이죠. 이럴 때, 중앙 버전 관리 시스템을 사용하는 경우엔 작동이 되는 버그없는 프로그램을 다시 받아야 합니다. 분산 관리 시스템일 경우에는 원하는 버전만 로컬 컴퓨터로 받아내면 되죠. 하지만 클로닝은 작업 중인 디렉토리 포함 그 디렉토리의 히스토리를 어느 선까지는 같이 다운로드 받게 합니다. Git은 최대한 효율성있게 시스템이 디자인되어 있지만, 클로닝 명령어를 쓴다면 프로젝트 파일들이 (비효율적으로) 현재 작업 중인 디렉토리에 전부 다시 생성될 것입니다. *해답*: Git은 이런 상황에서 좀 더 빠르고 공간적으로 효율성있게 클로닝을 할 수 있는 명령어를 가지고 있습니다: *git branch* 이런 환상적인 명령어를 이용하여 디렉토리에 있는 파일들은 탈바꿈을 감행해 이 버전과 저 버전을 넘나들 수 있습니다. 이 변형기법은 버전 사이를 넘나드는 것 외에도 더 많은 것을 할 수 있습니다. 당신의 파일들은 전 버전에서 실험할 있는 임시버전, 개발버전, 친구들이 보유하고 있는 버전 등으로 변형할 수 있습니다. === 일하는 척 하기 버튼 === 버튼 하나만 누르면 ("일하는 척 하기 버튼") 게임화면이 최소화되고 엑셀파일이 화면상에 나타나는 기능을 보신 적이 있을겁니다. 이 기능을 활용하면 직장상사의 눈을 속이고 일하던 척 할 수 있지요? 아무 디렉토리에서: $ echo "I'm smarter than my boss" > myfile.txt # 난 내 상사보다 똑똑하다 $ git init $ git add . $ git commit -m "Initial commit" 우리는 "난 내 상사보다 똑똑하다"라는 내용을 가진 텍스트파일을 Git 저장소에 만들었습니다. 그리고: $ git checkout -b boss # 이 명령어를 사용한 후엔 아무것도 바뀌지 않은 것처럼 보일겁니다. $ echo "My boss is smarter than me" > myfile.txt # 상사는 나보다 똑똑합니다 $ git commit -a -m "Another commit" 겉으로 보기에는 그 텍스트파일을 새로운 (맘에 들지않는) 문장으로 덮어씌우고 commit을 한 것처럼 보일겁니다. 그러나 그건 착각입니다. 다음 명령어를 입력해보세요: $ git checkout master # 처음 버전으로 돌아가기 자! 그럼 처음 생성했던 텍스트파일이 돌아왔을 겁니다. 만약에 그 상사가 이 사실을 알아채고 당신의 디렉토리를 살펴본다고 할 때는: $ git checkout boss # 아까 두 번째로 만들어놓은 "상사는 나보다 똑똑합니다"라는 메세지를 담은 myfile.txt 파일로 돌아갑니다. 이런 식으로 두 가지 다른버전의 파일 사이를 오갈 수 있습니다. 그리고 각각 따로 commit을 할 수 있지요. === 힘든 작업 === [[branch]] 당신이 어떤 작업을하고 있다고 가정합니다. 작업 도중에 세 버전 전으로 돌아가서 새로운 print 라인을 넣고 테스팅 해보고 싶다는 생각이 들었습니다. 그럴 때엔: $ git commit -a $ git checkout HEAD~3 이제 테스팅하고 싶었던 파일에 더하고 싶은 것을 걱정없이 마구 넣어도 됩니다. 이 미친 짓(?)을 Commit 해놓을 수도 있습니다. 작업이 다 끝났다면, $ git checkout master 를 사용해 아까 미친 짓을 하기 전의 작업상태로 돌아올 수 있습니다. Commit하지 않았던 작업들이 같이 딸려 왔다는 것을 확인 (조심!)할 수 있을 겁니다. 아까 그 임시작업 (미친 짓)을 세이브하고 싶다면 어떻게 해야할까요? 쉽습니다: $ git checkout -b dirty 를 실행하여 그 나뭇가지 (branch) 에서 마스터 나뭇가지로 돌아오기 전에 commit을 하면 됩니다. 그런 후 다시 미친 짓을 할 때의 상태로 돌아가고 싶다면: $ git checkout dirty 우리는 이 체크아웃이라는 명령어를 전에도 설명했었죠. 여기서는 이 명령어가 어떻게 예전 버전들을 불러오는 지 살펴볼 수 있었습니다: 파일을 원하는 버전으로 돌아가게 할 수 있으나, master 나뭇가지를 우선 벗어나야 하지요. 벗어난 후의 commit은 master 나뭇가지와는 다른 길을 걷게 될 것입니다. 그 길을 나중에 이름도 지어줄 수 있지요. 다시 말하면, 예전 상태 (state)에서 벗어나면 Git은 자동으로 이름이 (아직) 붙여지지 않은 새로운 나뭇가지로 이동시켜 줍니다. 이 나뭇가지는 *git checkout -b*로 이름을 바꿔 저장해줄 수 있죠. === 빠른 해결책 === 작업 중에 갑자기 하던 일을 멈추고 '1b6d...'commit에 있는 버그를 고치라고 부탁을 받았다고 생각해 봅시다: $ git commit -a $ git checkout -b fixes 1b6d 버그를 다 고친 후에: $ git commit -a -m "Bug fixed" $ git checkout master 이제 아까 잠시 중단했던 작업으로 돌아갈 수 있습니다. 버그가 고쳐진 파일도 병합해올 수 있죠: $ git merge fixes === 병합 (Merging) === Git을 제외한 어떤 버전 컨트롤 시스템들을 이용할 땐 나뭇가지 (branch)들을 만드는 것은 쉽지만 나뭇가지들을 병합하기는 어려울지도 모릅니다. Git에서는 병합작업이 정말 쉽고 병합이 진행되고 있는 중인지도 모르는 사이에 끝날 것입니다. 우리는 병합을 아까 전에도 소개했었습니다. 당겨오기 (*pull*) 명령어는 commit들을 가져와 지금 사용중인 나뭇가지에 병합하여 줍니다. 로컬에서 아무런 편집작업을 진행하지 않았더라면 *pull*은 현 나뭇가지를 '빨리 감기' 하여 중앙 서버에서 가장 최근의 정보를 가져와 병합합니다. 로컬에서 편집작업을 한 기록이 있다면, Git은 자동으로 병합을 시도할 것이고, 병합에 버전간의 차질이 있다면 당신에게 보고할 것 입니다. Commit은 보통 하나의 '부모 commit'이 있습니다. 병합을 다르게 생각해보면 한 commit이 적어도 두 개의 '부모 commit'이 있다고 생각할 수 있는 것이죠. 그럼 'HEAD~10'은 어떤 commit을 가르키는 걸까요? 부모가 하나가 아니라면 어떤 것을 거슬러 올라가야 전 버전에서 작업할 수 있을까요? Git은 먼저 commit되었던 부모를 따르게 설정되어 있습니다. 현재 작업중인 나뭇가지가 병합이 실행될 경우 첫번째 부모가 되기때문에 당연한 겁니다.: 당신은 언제나 현 나뭇가지에 가장 최근에 한 작업에만 관심이 있을 수 밖에 없기 때문이지요. 다른 나뭇가지에서 한 작업은 다음 일입니다. 탈자 기호 (^)를 이용하서 부모를 수동으로 정해줄 수도 있습니다. 예를 들어 두번째 부모의 기록을 조회하고 싶다면: $ git log HEAD^2 첫번째 부모의 기록을 조회할 때는 탈자기호 이후의 번호는 생략해도 됩니다. 굳이 보여드리자면: $ git diff HEAD^ 이 표기법은 다른 형식의 표기법과도 병행해서 사용할 수 있습니다: $ git checkout 1b6d^^2~10 -b ancient (집중하십시오) 새로운 나뭇가지인 "ancient"를 시작하고 두번째 부모의 첫번째 부모 나뭇가지에서 1b6d로 시작하는 commit과 그 commit 전 10개의 commit을 불러와 줄 것입니다. === 방해받지 않는 작업진행 === 하드웨어 관련작업을 하다보면 현재 작업중인 단계가 완료되어야만 다음 단계 진행이 가능할 것입니다. 자동차를 예로들면 외부로부터 오기로했던 부품들이 도착해야 비로소 수리에 들어갈 수 있겠지요. 프로토타입들은 칩들이 가공되어야 건축이 가능해 지겠죠. 소프트웨어 관련작업도 비슷합니다. 다음 작업이 진행될려면 현재 작업이 이미 발표 및 테스트가 되어있어야 할 겁니다. 어떤 작업들은 당신의 코드가 받아 들여지기 전 검토부터 되어야 겠지요. 그래서 당신은 그 검토가 끌날 때까지는 다음 작업으로 진행하지 못 할것입니다. 하지만 나뭇가지와 병합기능 덕분에 이 규치을 깨고 파트 1이 완료되기도 전에 파트 2에서 미리 작업을 진행하고 있을 수 있습니다. 파트 1을 commit하고 검토를 위해 어디론가 보냈다고 생각하십시오. Master 나뭇가지에서 있었다면, 그 나뭇가지에서 다른 나뭇가지로 갈아타야합니다: $ git checkout -b part2 그리곤 파트 2에서 commit을 하며 작업을 계속 진행하세요. 인간은 실수를 많이하는 동물이기에 파트 1으로 다시 돌아가서 무엇인가 고치고 싶을지도 모릅니다. 만약에 당신이 천재적인 프로그래머라면 다음 명령어를 사용할 일은 없겠지요. $ git checkout master # 파트 1로 돌아갑니다. $ fix_problem # 수정 작업 $ git commit -a # 수정 작업을 commit합니다. $ git checkout part2 # 파트 2로 다시 갑니다. $ git merge master # 아까 파트 1의 수정을 파트 2로 병합합니다. 이 때 즈음이면 이미 파트 1은 허가 받았겠지요. $ git checkout master # 파트 1로 돌아갑니다. $ submit files # 파일 배포! $ git merge part2 # 파트 2도 파트 1으로 병합. $ git branch -d part2 # 파트 2 나뭇가지 삭제. 이제 파트 2의 모든 것과 함께 master 나뭇가지로 돌아왔습니다. 나뭇가지는 제한 없이 원하는 만큼 생성할 수 있습니다. 거꾸로도 나뭇가지를 만들 수도 있죠: 만약에 7번의 commit전에 나뭇가지를 하나 만들어 놓았어야 함을 늦게 깨닫았을 때, 다음 명령어를 이용해 보세요: $ git branch -m master part2 # master 나뭇가지의 이름을 part2로 바꿉니다. $ git branch master HEAD~7 # 7 commit 전의 상황에서 master 나뭇가지를 새로 만듭니다. Master 나뭇가지는 이제 part 1만 들어있고, 나머지는 모두 part 2에 들어가게 되었습니다. 그리고 우리는 지금 part 2에서 작업을 하고 있는 중이겠지요; master를 만들면서 master로는 현재 작업공간을 옮겨가지 않았습니다. 처음 보시죠? 여태까지 설명한 예제들에서는 나뭇가지를 만들면서 곧바로 작업공간도 같이 옮겨갔었는데 말이죠. 이런 식으로요: $ git checkout HEAD~7 -b master # 나뭇가지를 만들고 바로 작업공간도 그 나뭇가지로 옮긴다. === 메들리의 재정리 === 하나의 나뭇가지에서 모든 작업을 끝내고 싶을 수도 있습니다. 작업중인 일들은 혼자만 알고 중요한 commit들만 다른사람들에게 보여주고 싶을 수 있습니다. 그럴경우엔 두 개의 나뭇가지를 우선 만드세요: $ git branch sanitized # 정돈된 commit을 보여주기 위한 나뭇가지를 만듭니다. $ git checkout -b medley # 작업을 하게 될 "메들리" 나뭇가지를 만들어 이동합니다. 버그를 고치던, 어떤 기능을 더하던, 임시코드를 더하던 작업을 진행합니다. 물론 commit을 해가면서 말이죠. 그리고: $ git checkout sanitized $ git cherry-pick medley^^ 위의 명령어들을 차례로 사용한다면 "메들리" 나뭇가지의 commit들을 "sanitzed" 나뭇가지에 붙입니다. "cherry-pick"명령어를 잘 사용한다면 영구적인 코드들만 들어있는 나뭇가지를 만들 수 있습니다. 그리고 그 commit들은 서로 연계가 잘 되어있을 것입니다. === 나뭇가지 관리하기 === 여태까지 프로젝트에서 생성한 나뭇가지들을 보려면: $ git branch 기본적으로 "master" 나뭇가지에서 작업을 시작하는 것이 디폴트로 지정되어 있습니다. 그러나 어떤 개발자들은 "master" 나뭇가지는 그대로 냅두고 새로운 나뭇가지를 만들어서 그 곳에서 작업하는 것을 선호합니다. *-d*와 *-m* 옵션들은 각각 나뭇가지들을 지우거나 이름을 바꿔줄 수 있는 파라메터들 입니다. *git help branch*를 보시면 더욱 자세히 설명되어 있을겁니다 (번역 주: 어차피 영어입니다) "master" 나뭇가지는 유용한 관례적인 이름의 나뭇가지일 뿐입니다. 다른 개발자들은 당신의 저장소에 당연히 "master"라는 이름을 가진 나뭇가지가 있을 것이라고 생각하겠지요. 그리고 그 나뭇가지는 모든 공식적인 자료들일 들어있다고 넘겨짚을 것입니다. 그러나 당신은 "master"를 없에거나 새로운 이름을 지정해줄 수 있으나, "master" 나뭇가지를 쓰는 관례를 따르는 것을 추천합니다. === 임시 나뭇가지 === Git을 사용하다보면 당신은 쓸모없는 하루살이의 나뭇가지들을 많이 만들고 있다는 사실을 깨달을 것입니다. 이유는 다음과 같겠지요: 그 많은 나뭇가지들은 작업의 경과를 저장하기 위해 만들어 놓고 무엇인가 고칠 것이 있을 때 빨리 돌가가기 위해서 쌓아두기만 하고있는 거겠죠. 다른 TV채널에서 무얼하나 확인할 때 잠시 채널을 바꾸는 것과 같은 아이디어입니다. 그러나 리모트 버튼 몇 개 누르면 되는 것과는 달리, 많은 나뭇가지를 만들고, 설정하고, 병합하고, 나중에 다쓰면 지워야합니다. 다행히도 Git에서는 TV 리모트와 비슷하게 지름길이 있습니다: $ git stash 이 명령어는 현 버전을 임시저장소 (stash)에 저장해 주고 작업하기 전의 상태로 돌아갑니다. 작업중인 디렉토리는 작업 (편집, 버그고침 등) 하기 전의 상태로 돌아가겠지요. 그리고 임시 (stash)로 돌아가고 싶다면: $ git stash apply # 에러 (version conflict)가 날지도 몰라요. 물론 여러개의 임시저장소 (stash)를 만들수도 있습니다. *git help stash*에 설명이 되어있으니 읽어보세요. 눈치챘을지 모르겠지만, Git은 올바른 임시저장소 (stash) 기능을 쓰게 해주기 위해서 나뭇가지들을 몰래 이용한답니다. === 원하는 방식대로 작업하기 === 나뭇가지를 이용하는 것이 꼭 필요한지 생각할지도 모르겠습니다. 파일들을 클로닝하는게 제일 빠르고 *cd*를 이용해 디렉토리를 바꿈으로써 나뭇가지를 대체하고 싶을지도 모릅니다. 웹브라우저의 예를 들어보겠습니다. 여러개의 창 아니면 여러개의 탭을 지원하는 이유는 무엇일까요? 여러 이용자들의 작업방식을 존중하여 주기 위해서랍니다. 어떤 이용자들은 웹브라우저 창 하나만 열고 여러 탭을 열어서 작업하는 방식을 추구합니다. 다른 이용자들은 반대의 형식으로 작업하는 것을 추구할지도 모르죠: 여러개의 창을 만들고 탭이 없이 작업하는 것을 말이죠. 또 어떤 이용자들은 이 두 방법들을 섞어서 작업하는 걸 선호할지도 모릅니다. 나뭇가지들은 마치 작업중인 디렉토리의 탭과 같습니다. 클로닝은 새로운 브라우저 창을 여는 것과 같은 것이죠. 이 두가지 방법은 모두 빠르고 로컬에서 진행됩니다. 그러니 당신에게 맞는 방법을 찾아보는 건 어떨까요? Git은 당신이 원하는 대로 일하게 도와줄 것입니다. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/secrets.txt��������������������������������������������������������������������0000644�0001750�0001750�00000026511�12666307504�015627� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Secrets Revealed == We take a peek under the hood and explain how Git performs its miracles. I will skimp over details. For in-depth descriptions refer to http://schacon.github.com/git/user-manual.html[the user manual]. === Invisibility === How can Git be so unobtrusive? Aside from occasional commits and merges, you can work as if you were unaware that version control exists. That is, until you need it, and that's when you're glad Git was watching over you the whole time. Other version control systems force you to constantly struggle with red tape and bureaucracy. Permissions of files may be read-only unless you explicitly tell a central server which files you intend to edit. The most basic commands may slow to a crawl as the number of users increases. Work grinds to a halt when the network or the central server goes down. In contrast, Git simply keeps the history of your project in the `.git` directory in your working directory. This is your own copy of the history, so you can stay offline until you want to communicate with others. You have total control over the fate of your files because Git can easily recreate a saved state from `.git` at any time. === Integrity === Most people associate cryptography with keeping information secret, but another equally important goal is keeping information safe. Proper use of cryptographic hash functions can prevent accidental or malicious data corruption. A SHA1 hash can be thought of as a unique 160-bit ID number for every string of bytes you'll encounter in your life. Actually more than that: every string of bytes that any human will ever use over many lifetimes. As a SHA1 hash is itself a string of bytes, we can hash strings of bytes containing other hashes. This simple observation is surprisingly useful: look up 'hash chains'. We'll later see how Git uses it to efficiently guarantee data integrity. Briefly, Git keeps your data in the `.git/objects` subdirectory, where instead of normal filenames, you'll find only IDs. By using IDs as filenames, as well as a few lockfiles and timestamping tricks, Git transforms any humble filesystem into an efficient and robust database. === Intelligence === How does Git know you renamed a file, even though you never mentioned the fact explicitly? Sure, you may have run *git mv*, but that is exactly the same as a *git rm* followed by a *git add*. Git heuristically ferrets out renames and copies between successive versions. In fact, it can detect chunks of code being moved or copied around between files! Though it cannot cover all cases, it does a decent job, and this feature is always improving. If it fails to work for you, try options enabling more expensive copy detection, and consider upgrading. === Indexing === For every tracked file, Git records information such as its size, creation time and last modification time in a file known as the 'index'. To determine whether a file has changed, Git compares its current stats with those cached in the index. If they match, then Git can skip reading the file again. Since stat calls are considerably faster than file reads, if you only edit a few files, Git can update its state in almost no time. We stated earlier that the index is a staging area. Why is a bunch of file stats a staging area? Because the add command puts files into Git's database and updates these stats, while the commit command, without options, creates a commit based only on these stats and the files already in the database. === Git's Origins === This http://lkml.org/lkml/2005/4/6/121[Linux Kernel Mailing List post] describes the chain of events that led to Git. The entire thread is a fascinating archaeological site for Git historians. === The Object Database === Every version of your data is kept in the 'object database', which lives in the subdirectory `.git/objects`; the other residents of `.git/` hold lesser data: the index, branch names, tags, configuration options, logs, the current location of the head commit, and so on. The object database is elementary yet elegant, and the source of Git's power. Each file within `.git/objects` is an 'object'. There are 3 kinds of objects that concern us: 'blob' objects, 'tree' objects, and 'commit' objects. === Blobs === First, a magic trick. Pick a filename, any filename. In an empty directory: $ echo sweet > YOUR_FILENAME $ git init $ git add . $ find .git/objects -type f You'll see +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. How do I know this without knowing the filename? It's because the SHA1 hash of: "blob" SP "6" NUL "sweet" LF is aa823728ea7d592acc69b36875a482cdf3fd5c8d, where SP is a space, NUL is a zero byte and LF is a linefeed. You can verify this by typing: $ printf "blob 6\000sweet\n" | sha1sum Git is 'content-addressable': files are not stored according to their filename, but rather by the hash of the data they contain, in a file we call a 'blob object'. We can think of the hash as a unique ID for a file's contents, so in a sense we are addressing files by their content. The initial `blob 6` is merely a header consisting of the object type and its length in bytes; it simplifies internal bookkeeping. Thus I could easily predict what you would see. The file's name is irrelevant: only the data inside is used to construct the blob object. You may be wondering what happens to identical files. Try adding copies of your file, with any filenames whatsoever. The contents of +.git/objects+ stay the same no matter how many you add. Git only stores the data once. By the way, the files within +.git/objects+ are compressed with zlib so you should not stare at them directly. Filter them through http://www.zlib.net/zpipe.c[zpipe -d], or type: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d which pretty-prints the given object. === Trees === But where are the filenames? They must be stored somewhere at some stage. Git gets around to the filenames during a commit: $ git commit # Type some message. $ find .git/objects -type f You should now see 3 objects. This time I cannot tell you what the 2 new files are, as it partly depends on the filename you picked. We'll proceed assuming you chose ``rose''. If you didn't, you can rewrite history to make it look like you did: $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose' $ find .git/objects -type f Now you should see the file +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, because this is the SHA1 hash of its contents: "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Check this file does indeed contain the above by typing: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch With zpipe, it's easy to verify the hash: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum Hash verification is trickier via cat-file because its output contains more than the raw uncompressed object file. This file is a 'tree' object: a list of tuples consisting of a file type, a filename, and a hash. In our example, the file type is 100644, which means `rose` is a normal file, and the hash is the blob object that contains the contents of `rose'. Other possible file types are executables, symlinks or directories. In the last case, the hash points to a tree object. If you ran filter-branch, you'll have old objects you no longer need. Although they will be jettisoned automatically once the grace period expires, we'll delete them now to make our toy example easier to follow: $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune For real projects you should typically avoid commands like this, as you are destroying backups. If you want a clean repository, it is usually best to make a fresh clone. Also, take care when directly manipulating +.git+: what if a Git command is running at the same time, or a sudden power outage occurs? In general, refs should be deleted with *git update-ref -d*, though usually it's safe to remove +refs/original+ by hand. === Commits === We've explained 2 of the 3 objects. The third is a 'commit' object. Its contents depend on the commit message as well as the date and time it was created. To match what we have here, we'll have to tweak it a little: $ git commit --amend -m Shakespeare # Change the commit message. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Rig timestamps and authors. $ find .git/objects -type f You should now see +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ which is the SHA1 hash of its contents: "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice <alice@example.com> 1234567890 -0800" LF "committer Bob <bob@example.com> 1234567890 -0800" LF LF "Shakespeare" LF As before, you can run zpipe or cat-file to see for yourself. This is the first commit, so there are no parent commits, but later commits will always contain at least one line identifying a parent commit. === Indistinguishable From Magic === Git's secrets seem too simple. It looks like you could mix together a few shell scripts and add a dash of C code to cook it up in a matter of hours: a melange of basic filesystem operations and SHA1 hashing, garnished with lock files and fsyncs for robustness. In fact, this accurately describes the earliest versions of Git. Nonetheless, apart from ingenious packing tricks to save space, and ingenious indexing tricks to save time, we now know how Git deftly changes a filesystem into a database perfect for version control. For example, if any file within the object database is corrupted by a disk error, then its hash will no longer match, alerting us to the problem. By hashing hashes of other objects, we maintain integrity at all levels. Commits are atomic, that is, a commit can never only partially record changes: we can only compute the hash of a commit and store it in the database after we already have stored all relevant trees, blobs and parent commits. The object database is immune to unexpected interruptions such as power outages. We defeat even the most devious adversaries. Suppose somebody attempts to stealthily modify the contents of a file in an ancient version of a project. To keep the object database looking healthy, they must also change the hash of the corresponding blob object since it's now a different string of bytes. This means they'll have to change the hash of any tree object referencing the file, and in turn change the hash of all commit objects involving such a tree, in addition to the hashes of all the descendants of these commits. This implies the hash of the official head differs to that of the bad repository. By following the trail of mismatching hashes we can pinpoint the mutilated file, as well as the commit where it was first corrupted. In short, so long as the 20 bytes representing the last commit are safe, it's impossible to tamper with a Git repository. What about Git's famous features? Branching? Merging? Tags? Mere details. The current head is kept in the file +.git/HEAD+, which contains a hash of a commit object. The hash gets updated during a commit as well as many other commands. Branches are almost the same: they are files in +.git/refs/heads+. Tags too: they live in +.git/refs/tags+ but they are updated by a different set of commands. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/multiplayer.txt����������������������������������������������������������������0000644�0001750�0001750�00000027452�12666307504�016533� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Git은 멀티플레이어 == 제가 과거 프리랜서 시절부터 Git을 개인적인 용도로 사용해 오고있었습니다. 여태까지 소개했던 많은 명령어들 중, 당시에는 *pull*과 *clone정도만 사용하여 같은 프로젝트를 여러 디렉토리에 저장하는데 사용하였습니다. 시간이 지난 후 Git에 제가 만든 코드를 올리고 싶었고 다른 개발자들이 한 작업도 반영하고 싶었습니다. 저는 전 세계의 많은 개발자들을 관리하는 방법을 배워야 했습니다. 다행히도 이런 일을 도와주는 것은 Git의 가장 큰 힘입니다. Git이 존재하는 이유이기도 하지요. === 난 누굴까? === 각 commit은 작성자의 이름과 작성자의 이메일주소를 저장합니다. *git log*를 사용해 조회할 수 있습니다. 기본설정 상, Git은 시스템 세팅을 이용해 작성자의 이름과 이메일주소를 저장합니다. 수동으로 이름과 이메일주소를 설정하려면: $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com 현재 사용중인 저장소에만 사용할 수 있는 작성자 이름이나 이메일을 설정하려면 위 명령어는 사용하지마세요. === Git 을 통한 SSH, HTTP 연결 === 웹 서버에 관한 SSH 접근권한을 보유하고 있다고 합니다. Git은 아직 설치되어 있지않다고 가정합니다. 기존 프로토콜만큼 효율적이진 않겠지만, Git은 HTTP를 통해 데이터 교환이 가능합니다. Git을 다운받아서 설치합니다. 그리고 웹 디렉토리에 저장소를 만듭니다: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update Git의 예전 버전에선 복사를 지시하는 명령어가 안 들을 수 있습니다. 그렇다면: $ chmod a+x hooks/post-update 이제 아무 클론에서 SSH를 통해 당신의 작업을 업로드할 수 있습니다: $ git push web.server:/path/to/proj.git master 다른 사람들은 당신의 작업을 다운받으려면 다음 명령어를 쓰면 될겁니다: $ git clone http://web.server/proj.git === 모든 것은 Git을 통한다 === 서버와의 연결없이 저장소를 동기화시키고 싶다고요? 긴급하게 수정할 것이 발견되었다고요? <<makinghistory, *git fast-export* 그리고 *git fast-import* 명령어들은 저장소를 하나의 파일로 묶어주거나 그 하나의 파일을 저장소로 되돌려 줄 수 있는 것을 배웠습니다>>. 여러가지 매개체를 통해서 저장소의 파일들을 옮길 수 있지만, 정말 효율적인 방법은 *git bundle* 명령어를 쓰는 것입니다. 보내는 이가 '묶음 (bundle)'을 만듭니다: $ git bundle create somefile HEAD 그리고 다른 장소로 그 묶음, +somefile+을 어떻게든 옮겨야합니다: 이메일, USB드라이브, *xxd* 프린트, OCR 스캐너, 전화로 이야기하던지, 연기로 신호를 보내던지 등 어떻게든 보내야합니다. 파일을 받을 사람들은 이 묶음으로부터의 commit을 다음 명령어를 이용하여 받을 수 있습니다: $ git pull somefile 파일을 받는 사람들은 빈 저장소에서도 이 명령어를 사용할 수 있습니다. 파일의 사이즈에도 불구하고 +somefile+ 은 저장소의 본 모습을 담고 있습니다. 큰 프로젝트에서는 묶음만들기를 좀 더 효율적으로 하기위해서 버전차이만을 묶어줍니다. 예를 들어서 "1b6d"를 commit 이 가장 최근에 공유된 commit이라고 가정해 봅니다: $ git bundle create somefile HEAD ^1b6d 너무 자주 이렇게 한다면, 어떤 commit이 가장 최근 것인지 기억하지 못할 수 있습니다. 도움말에서는 태그를 이용해 이런 문제점들을 피하라 명시합니다. 다시 말하자면 어떤 묶음을 보낸 후에는: $ git tag -f lastbundle HEAD 그리고 새로운 묶음을 만들어 줍니다: $ git bundle create newbundle HEAD ^lastbundle === 패치: 세계적 통화 === 패치는 컴퓨터와 인간이 쉽게 알아들을 수 있는 언어로 파일의 변화를 텍스트로 표현할 수 있는 방법입니다. 이런 식으로 세계적으로 다양하게 사용되고 있습니다. 어떠한 버전 관리 시스템을 쓰던간에 개발자들에게 패치를 이메일로 보낼 수 있습니다. 그 개발자들이 그 이메일을 읽을 수만 있다면 그들은 당신이 편집을 한 작업기록을 볼 수 있습니다. 첫 장에서 본 명령어를 다시 한번 해봅시다: $ git diff 1b6d > my.patch 위 명령어는 이메일로 추후에 토론할 수 있게 패치를 붙여넣기 하여 공유할 수동으로 있었습니다. Git 저장소에서 다음을 따라해보세요: $ git apply < my.patch 위 명령어를 사용하여 패치를 적용시킵니다. 작성자의 이름과 싸인이 기록되어야하는 좀 더 공식적인 환경에서는 그에 상응하는 패치 (commit 1b6d 이후)를 만들기위해 다음 명령어를 사용합니다: $ git format-patch 1b6d 이렇게 만든 파일묶음은 *git-send-email*을 사용하여 보낼 수 있습니다. 보내고싶은 commit 묶음을 수동으로 지정해줄 수도 있습니다: $ git format-patch 1b6d..HEAD^^ 받는 쪽에서는 이메일을 받을 때: $ git am < email.txt 이 명령어는 새로받은 패치를 적용시키고 작성자의 정보가 포함된 새로운 commit을 만듭니다. 패치를 받기전에, 당신의 이메일 클라이언트에서 이메일에 첨부된 commit 묶음이 어떤 사람에 의해 포맷이 바뀌진 않았는지 확인합니다. mbox-를 기반으로하는 이메일 클라이언트는 약간의 문제점들이 있습니다. 그러나 이런 방식의 클라이언트를 쓸만한 사람이라면 손쉽게 튜토리얼을 읽지않고도 해결할 수 있을것입니다. === 죄송합니다. 주소를 옮겼습니다 === 저장소를 클로닝한 후 *git push*나 *git pull*을 사용하면 원래의 URL에서 해당 명령어를 실행합니다. Git은 어떤 원리로 이렇게 하는 것일까요? 그 비밀은 클론을 만들때 생선된 config 옵션에서 찾을 수 있습니다. 한번 볼까요?: $ git config --list +remote.origin.url+ 옵션은 URL 소스를 통제합니다; "origin"은 원래 저장소에 붙여진 별명이라고 보면됩니다. 나뭇가지에는 "master"라고 이름이 붙듯이 말이죠. 그말은 이 이름을 바꾸거나 지울 수 있는데 할 필요는 없다는 것입니다. 제일 처음 사용하던 저장소가 옮겨지면, URL을 수정해 주어야 합니다: $ git config remote.origin.url git://new.url/proj.git +brach.master.merge+ 옵션은 *git pull*로 당겨올 수 있는 나뭇가지를 설정하여 줍니다. 처음으로 클론을 생성하였을때, 그 클론의 나뭇가지는 그 클론을 만들어온 저장소의 현재 사용중인 저장소와 같게 설정이 되어있습니다. 그렇기 때문에 현재 작업 헤드가 다른 나뭇가지로 옮겨갔었다고 하더라도, 추후의 당겨오기는 본래의 나뭇가지를 따를 수 있게 해줄 것 입니다. 본 옵션은 처음에 +branch.master.remote+옵션에 기록되어 있는 클론에만 적용됩니다. 다른 저장소에서 당겨오기를 실행한다면, 구체적으로 어떤 나뭇가지에서 당겨오길 원하는지 설정해주어야 합니다: $ git pull git://example.com/other.git master 이 것은 왜 전에 보여드렸던 밀기와 당겨오기 예제에 다른 argument가 붙지 않았었는지 설명하여 줍니다. === 원격 나뭇가지 === 어떠한 저장소를 클론할 때에는 그 클론의 모든 나뭇가지를 클론하게 됩니다. Git은 이 사실을 은폐하기에 당신은 클론을 하면서 몰랐을지도 모릅니다: 그러니 당신은 직접 Git에게 물어보아야 합니다. 이 설정은 원격 저장소에 있는 나뭇가지들은 당신의 나뭇가지들을 꼬이게하는 일을 없게 해줍니다. 그래서 Git 역시 초보자들이 사용할 수 있는 것이고요. 다음 명령어를 이용하여 원격 나뭇가지들을 나열합니다: $ git branch -r 당신은 다음과 비슷한 결과물들을 보게될 것입니다: origin/HEAD origin/master origin/experimental 이 결과는 각 행마다 원격저장소의 나뭇가지와 현 작업위치를 돌려주는 결과이며, 다른 Git 명령어들과 함께 사용될 수 있습니다. 예를 들면, 당신은 지금 많은 commit을 하였다고 먼저 가정합니다. 그러고는 가장 최근에 가져온 버젼과 비교를 하고싶다고 생각해봅니다. SHA1 해쉬를 찾아서 확인할 수도 있지만 다음 명령어로 더 간단히 비교할 수 있습니다: $ git diff origin/HEAD 아니면 "experimental" 나뭇가지가 지금 어떠한 상태인지 알아낼 수도 있습니다. $ git log origin/experimental === 다수의 원격저장소 === 당신 외의 두명의 개발자가 프로젝트를 공동으로 진행하고 있다고 가정합니다. 그리고 그 둘의 작업상황을 주시하고 싶습니다. 당신은 다음 명령어를 사용함으로써 하나 이상의 저장소를 추적할 수 있습니다: $ git remote add other git://example.com/some_repo.git $ git pull other some_branch 이제 두번째 저장소의 나뭇가지로 병합을 시도하였으며 모든 저장소의 모든 나뭇가지에 대한 접근권한이 생겼습니다. $ git diff origin/experimental^ other/some_branch~5 그러나 내 작업과 관련없이 버전의 변화를 비교해내는 방법은 무엇일까요? 풀어말하자면 그들의 나뭇가지를 보는 동시에 내 작업이 영향받지않게 하고싶다는 것입니다. 그렇다면 당겨오기 보다는: $ git fetch # 원래의 저장소로부터 물어옵니다. 디폴트 명령어. $ git fetch other # 다른 개발자의 저장소를 물어옵니다. 버전기록들만을 가져오는 명령어들입니다. 현재 작업중인 디렉토리는 영향을 받지않을 것이지만, 로컬 사본을 가지고 있기에 우리는 이제 어느 저장소의 어떤 나뭇가지라도 Git 명령어를 사용하여 활용할 수 있습니다. Pull은 간단히 풀어서 설명하면 *fetch(물어오기)* 후 *merge(병합하기)*를 합친 하나의 고급명령어라고 말할 수 있습니다. 우리는 마지막 으로 한 commit을 현재 작업에 병합하길 원하기 때문에 주로 *pull(당겨오기)*를 사용하게 될 것입니다; 위에 설명한 상황은 특수상황이지요. *git help remote*에는 원격 저장소를 삭제하는 방법, 특정 나뭇가지를 무시하는 방법 외에 많은 것을 볼 수 있습니다. === 나만의 취향 === 나는 작업을 할때 다른 개발자들이 내가 당겨오기를 실행할 수 있게 항시 준비해두는 것을 선호합니다. 어떠한 Git 호스팅 서비스는 클릭 한 번만으로도 쉽게 이를 행할 수 있게 도와주는 것도 있습니다. 어떤 파일꾸러미를 물어온 후에는 Git 명령어들을 사용하여 프로젝트가 잘 정리되어 있길 빌며 변화 기록을 조회합니다. 그러고는 나의 작업을 병합합니다. 그 후 내 작업이 맘에 들 경우 메인 저장소에 밀어넣기 합니다. 다른 사람들로부터 많은 도움을 받는 스타일은 아니지만, 이러한 내 작업방식을 추천드리고 싶습니다. 다음 링크를 한 번보세요. http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[Linus Torvalds의 블로그 포스팅]. Git의 세상에 거주하는 것은 패치 파일들을 만들어 배포하는 것보다 더 효율적입니다. Git은 단순한 버전관리 외에도 작업을 행한 사람의 이름, 이메일주소, 작업날짜를 같이 기록하여줍니다.����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/preface.txt��������������������������������������������������������������������0000644�0001750�0001750�00000010740�12666307504�015561� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������= Git Magic = Ben Lynn 2007년 8월 == 서문 == http://git-scm.com/[Git] 은 버전 관리계의 스위스아미나이프 정도로 보면 됩니다. 아주 유연하고 믿을 수 있지만 그만큼 배우기는 어려울 수도있는 본 버전 관리 시스템을 마스터 해봅시다! Arthur C. Clarke는 충분히 발전한 기술은 마술과 같다고 말하였습니다. Git도 마찬가지입니다. 초보자들은 Git이 어떻게 돌아가는지 알 필요가 없으며 Git이라는 간단한 장치가 어떻게 친구들과 적들을 놀라게하는지만 알면됩니다. 세부사항들을 설명하는 대신에, 우리는 몇몇 기능들의 대략적인 설명을 하려합니다. 여기에 설명된 기능들을 자주 사용하다 보면 각각의 명령어들이 어떻게 작동하는지 알게 될 것입니다. 그리고 그 명령어들을 적용하여 새로운 일들을 해낼 수 있겠지요. .번역판 - link:/\~blynn/gitmagic/intl/zh_cn/[Simplified Chinese]: by JunJie, Meng and JiangWei. Converted to link:/~blynn/gitmagic/intl/zh_tw/[Traditional Chinese] via +cconv -f UTF8-CN -t UTF8-TW+. - link:/~blynn/gitmagic/intl/fr/[French]: by Alexandre Garel, Paul Gaborit, and Nicolas Deram. Also hosted at http://tutoriels.itaapy.com/[itaapy]. - link:/~blynn/gitmagic/intl/de/[German]: by Benjamin Bellee and Armin Stebich; also http://gitmagic.lordofbikes.de/[hosted on Armin's website]. - link:/~blynn/gitmagic/intl/it/[Italian]: by Mattia Rigotti. - link:/~blynn/gitmagic/intl/ko/[Korean]: by Jung-Ho (John) Han; also https://sites.google.com/site/drinkhanjohn/useful-links/[hosted on John's website]. - link:/~blynn/gitmagic/intl/pl/[Polish]: by Damian Michna. - link:/~blynn/gitmagic/intl/pt_br/[Brazilian Portuguese]: by José Inácio Serafini and Leonardo Siqueira Rodrigues. - link:/~blynn/gitmagic/intl/ru/[Russian]: by Tikhon Tarnavsky, Mikhail Dymskov, and others. - link:/~blynn/gitmagic/intl/es/[Spanish]: by Rodrigo Toledo and Ariset Llerena Tapia. - link:/~blynn/gitmagic/intl/uk/[Ukrainian]: by Volodymyr Bodenchuk. - link:/~blynn/gitmagic/intl/vi/[Vietnamese]: by Trần Ngọc Quân; also http://vnwildman.users.sourceforge.net/gitmagic/[hosted on his website]. .그 외의 에디션 - link:book.html[Single webpage]: CSS 없는 HTML 버전. - link:book.pdf[PDF file]: 프린팅 버전. - http://packages.debian.org/gitmagic[Debian package], http://packages.ubuntu.com/gitmagic[Ubuntu package]: 이 웹사이트의 사본. Handy http://csdcf.stanford.edu/status/[when this server is offline]. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Physical book [Amazon.com]]: 64 pages, 15.24cm x 22.86cm, black and white. 전기가 들어오지 않을 때 유용. === 고맙습니다! === 많은 분들 께서 번역에 힘써주셔서 저는 어떻게 몸둘 바를 모르겠습니다. 이 분들을 통해 더 많은 독자들을 만날 수 있어서 정말 기쁘고 감사드립니다. Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, Tyler Breisacher, Sonia Hamilton, Julian Haagsma, Romain Lespinasse, Sergey Litvinov, Oliver Ferrigni, David Toca, Сергей Сергеев, Joël Thieffry, and Baiju Muthukadan 수정 및 편집에 힘써주셧습니다.. François Marier 는 Daniel Baumann이 개발한 Debian 패키지를 관리합니다. 고마워 해야할 사람들이 많지만은 여기에 다 쓸수는 없는 노릇입니다. 그래도 만약에 제가 이 웹사이트에 실수로 이름을 개제하지 않았다면 연락을 주시거나 패치를 만들어 주세요! === 라이센스 === 이 가이드는 http://www.gnu.org/licenses/gpl-3.0.html[the GNU General Public License version 3] 통해 발간되었습니다. 자연스레 소스콛드들은 Git 저장소에 저장되어있습니다: $ git clone git://repo.or.cz/gitmagic.git # Creates "gitmagic" directory. 아니면 다음 미러사이트들에도 소스코드가 저장되어 있을겁니다.: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git $ git clone https://code.google.com/p/gitmagic/ $ git clone git://git.assembla.com/gitmagic.git $ git clone git@bitbucket.org:blynn/gitmagic.git GitHub, Assembla, Bitbucket은 사적인 저장소를 지지합니다. Assembla와 Bitbucket은 무료로 제공되고 있습니다. ��������������������������������gitmagic-20160304/ko/basic.txt����������������������������������������������������������������������0000644�0001750�0001750�00000024154�12666307504�015241� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������==기본적인 요령== Git 명령어의 바다 속에 곧바로 빠지는 것 보단, 다음 기본적인 예제를 통해서 천천히 배우는 방법이 좋을 것입니다. 표면적으로는 간단하게 보이지만, 이 곳 예제들은 앞으로 많은 도움이 될 것입니다. 저 역시도 처음 Git을 사용할 때에는 아래에 있는 예제 외에 다른 것들은 건들여 보지도 않았습니다. === 상태 (state) 저장하는 방법=== 무엇인가 대단한 것을 해보고 싶으시다고요? 그러시기 전에, 현 디렉토리에 들어있는 모든 파일의 스냅샷을 찍어봅시다: $ git init $ git add . $ git commit -m "My first backup" 만약에 편집을 하다가 잘 못됬다면, 예전의 편집되기 전의 깨끗한 버전을 되돌리면 됩니다: $ git reset --hard 다시 state를 저장하고 싶다면: $ git commit -a -m "Another backup" === 파일 더하기 (add), 지우기 (delete), 이름 바꾸기 (rename) === 위의 간단한 요령들은 처음 *git add* 명령어를 실행했을 때 이미 존재하던 파일들만 저장하게 됩니다. 새로운 파일들이나 하위 디렉토리들을 추가했다면: $ git add readme.txt Documentation 그리고 만약에 원하지 않는 파일을 Git에서 없애려면: $ git rm kludge.h obsolete.c $ git rm -r incriminating/evidence/ 이렇게 함으로써 Git은 지정한 파일들을 지워주게 됩니다. 파일 이름바꾸기는 원치않는 현재의 이름을 지우고 새로운 이름을 새롭게 지정하는 컨셉과 같습니다. 좀 더 손쉬운 방법으로는 *git mv* 명령어가 있습니다. 예를 들어: $ git mv bug.c feature.c === 고급 undo와 redo === 가끔씩은 작업을 하다가 하던 일을 멈추고 전 버전으로 돌아가고 싶다거나, 한 시점 이후의 모든 편집을 지우고 싶을 때가 있을 것입니다. 그렇다면: $ git log 이 명령어는 최근에 commit들을 정리한 리스트와 그의 SHA1을 보여줍니다. ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob <bob@example.com> Date: Tue Mar 14 01:59:26 2000 -0800 Replace printf() with write(). commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alice <alice@example.com> Date: Thu Jan 1 00:00:00 1970 +0000 Initial commit. ---------------------------------- Hash 앞의 알파벳 몇 개만으로도 commit을 세분화 설정하실 수 있습니다; 다른 방법으로는, hash 전문을 복사/붙여넣기 하는 방법도 있지요: $ git reset --hard 766f 위 명령어를 입력하시면 설정된 commit으로 돌아갈 수 있으며 그 후의 새로운 commit들은 영구적으로 삭제됩니다. 가끔씩은 또 아주 예전의 state로 잠시만 돌아가길 원하실 수 있습니다. 그럴 경우에는: $ git checkout 82f5 이 명령어는 새로운 commit들을 보존함과 동시에 과거의 시간으로 잠시 돌아가게 해줍니다. 그러나, SF영화에서 처럼, 과거에 돌아간 상태에서 편집을하고 commit을 한다면 다른 시간대의 현실을 만들어가게 되는 것이죠. 왜냐하면 당신의 편집이 과거의 편집과는 다르게 입력이 되었기 때문입니다. 이런 대체현실을 'branch (나뭇가지)'라고 부릅니다 <<branch>>에 관해선 추후에 자세히 설명합니다>>. 지금 알고계셔야 할 것은 $ git checkout master 이 것은 현재시간의 state로 오게 해줄 것입니다. 그리고 Git이 푸념을 놓기전에 편집했던 사항들이 있다면 master branch로 돌아오기전 commit을 하거나 reset을 하시길 바랍니다. 컴퓨터 게임과 또 다시 비교해본다 하면: - *`git reset --hard`*: 예전에 세이브 해뒀던 게임으로 돌아가며, 돌아간 시점 이후의 세이브들을 모두 삭제합니다. - *`git checkout`*: 예전에 세이브 해뒀던 게임으로 돌아가며, 돌아간 시점 이후의 게임들은 처음 세이브와 다른 길을 가게 됩니다. 추후의 모든 세이브들은 다른 branch로써 새로운 현실세계를 만들게 됩니다 <<branch>>에 관해선 추후에 자세히 설명합니다>>. 예전의 파일/하위 디렉토리들을 되돌리고 싶을 때 다음 명령어를 이용함으로써 필요한 파일/하위 디렉토리만을 되돌릴 수 있습니다: $ git checkout 82f5 some.file another.file 그러나 이 *checkout* 핸들이 다른 파일들을 조용히 덮어씌우기 할 수 있다는 점을 알아두세요. 이러한 사고를 방지하고 싶다면 checkou 명령어를 쓰기전에 commit을 이용하세요. Git을 처음 이용하는 분들은 특히 더 조심하시기 바랍니다. 대체적으로 파일이 삭제될까 두려우시다면 *git commit -a*를 우선해놓고 생각하세요. Hash를 자르고 붙여넣기 싫으시다고요? 그렇다면: $ git checkout :/"My first b" 이 명령어를 사용함으로써 이 message로 commit을 해두었던 state로 돌아갈 수 있습니다. 그리고 이 다음 명령어로 5번 스텝 전의 state로 돌아갈 수도 있습니다: $ git checkout master~5 === 되돌리기 (Reverting) === 법정에서는 어떠한 일에 관해서는 기록에서 지울 수 있습니다. 이런 식으로, Git에서는 원하는 commit을 정해서 없던 일로 할 수 있습니다. $ git commit -a $ git revert 1b6d 이렇게 하는 것으로 특정 hash에 대한 commit을 undo 할 수 있습니다. 이렇게 되돌린 state는 새로운 commit으로 인식되어 *git log*에 기록됩니다. === 변경기록 만들기 === 어떤 프로젝트들은 http://en.wikipedia.org/wiki/Changelog[changelog]. 필요로 합니다. 다음 명령어를 이용해 변경기록을 만들어 봅시다.: $ git log > ChangeLog === 파일 다운로드하기 === Git으로 관리되는 프로젝트 사본을 얻기위해서는: $ git clone git://server/path/to/files 예를 들어, 본 웹사이트를 만들기 위해 사용한 파일들을 얻기위해서는: $ git clone git://git.or.cz/gitmagic.git 곧 *clone* 명령어에 관해 많은 것을 소개하도록 하겠습니다. === 최첨단 기술 === *git clone* 명령어를 이용해 어떤 프로젝트의 사본을 다운로드했다면, 다음 명령어를 이용해 그 프로젝트의 최신버전으로 업그레이드 할 수 있습니다: $ git pull === 즉석 발행 === 당신이 다른 사람들과 공유하고 싶은 스크립트를 작성했다고 가정합니다. 당신은 그들에게 당신의 컴퓨터에서 다운로드를 받으라고 할 수있지만, 당신 친구들이 만약 당신이 편집하는 도중에 받게된다면, 그들은 예상치 못 한 트러블에 걸릴 수 있습니다. 이러한 이유 때문에 릴리스 사이클이란 것이 존재하는 것입니다. 개발자들은 개발 중인 프로젝트에 자주 들락날락 거릴 것이고, 그들은 남 앞에 내놓을 만한 프로젝트로 만들어지기 전까지 남들에게 보여주게 되지 않을겁니다. Git으로 이런 문제를 해결할려면, 당신의 스크립트가 들어있는 디렉토리에서: $ git init $ git add . $ git commit -m "First release" 그리고 당신들 친구들에게 다음 명령어를 사용하도록 하십시오: $ git clone your.computer:/path/to/script 그들이 이렇게하면 당신의 스크립트를 다운로드 할 수 있을 것입니다. 이 작업은 ssh 접근을 가정합니다. 그렇지 않다면, 당신은 *git daemon* 명령어를 쓴 후 친구들에게 다음 명령어를 써보라고 합니다: $ git clone git://your.computer/path/to/script 이렇게 하고 난 다음부터 당신의 스크립트가 준비되었을 때마다 다음 명령어를 실행하면 됩니다: $ git commit -a -m "Next release" 당신의 친구들은 다음 명령어를 사용함으로써 가장 최근 버전으로 당신의 스크립트를 보유하고 있을 수 있게 되죠: $ git pull 그들은 절대로 당신이 보여주고 싶지않은 버전의 스크립트를 보는 일이 없을 것입니다. === 제가 도대체 뭘 한거죠? === 마지막으로 한 commit으로 부터 어떤 변화가 있었는지 확인하기 위해서는: $ git diff 아니면 어제부터 어떤 변화가 있었는지 확인하기 위해서는: $ git diff "@{yesterday}" 아니면 어떤 특정 버전에서 부터 2번째 전 버전 사이의 변화를 확인하기 위해서는: $ git diff 1b6d "master~2" 각각의 결과는 *git apply*와 함께 적용할 수 있는 패치가 될 것입니다. 다음 명령어도 사용해 보세요: $ git whatchanged --since="2 weeks ago" 저는 가끔씩 http://sourceforge.net/projects/qgit[qgit] 에 들어가서 히스토리를 체크하곤 합니다. 이 웹사이트는 깨끗한 그래픽 인터페이스로 구성되어 있어 보기 쉽지요. 아니면, http://jonas.nitro.dk/tig/[tig], 텍스트형식 인터페이스 역시 느린 연결방식을 가지고 있는 분들에겐 도움이 될 것입니다. 또 다른 방법으로는 웹 서버를 설치한 후 *git instaweb*명령어를 사용하는 방법도 있겠지요. === 연습 === 우선 A, B, C, D 를 각각 연속된 commit이라고 가정합니다. 그리고 B는 A 에서 몇 개의 파일들이 삭제된 버전으로 가정합니다. 문제는 여기서 몇몇 파일들을 D에 더하고 싶을 때 어떻게 하는건가 입니다. 세가지의 해답을 찾을 수 있겠군요. 우선 우리가 현재 D에 있다고 생각합시다: 1. A와 B의 차이점은 몇 개의 파일들이 없어진 것 뿐입니다. 우리는 이 차이점을 패치로 작성하여 적용할 수 있습니다.: $ git diff B A | git apply 2. 우리는 A에 파일을 저장해 두었기에, 그 곳에서 다시 받아올 수 있겠지요: $ git checkout A foo.c bar.h 3. 또는 A에서 B까지로 갈 때의 변화를 undo한다고 생각하셔도 됩니다: $ git revert B 어떤 방법이 가장 좋은 해답일까요? 답은 본인이 원하는 것이 곧 해답입니다. Git을 이용한다면 당신이 원하는 것은 쉽게 해낼 수 있고, 그 것을 해내는 방법은 한가지만 있는 것이 아닐 겁니다. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/grandmaster.txt����������������������������������������������������������������0000644�0001750�0001750�00000032146�12666307504�016467� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Git 마스터링 == 지금까지 배운것만으로도 당신은 *git help* 페이지를 자유롭게 돌아다니며 거의 모든 것을 이해할 수 있을 것입니다. 그러나 어떠한 문제를 풀기위해 어느 한 가지의 알맞는 명령어를 찾는 것은 아직 어려울 수 있습니다. 그런 문제에 대해 제가 도와줄 수 있을 것 같습니다: 아래는 제가 Git을 사용하며 유용하게 썼던 몇가지 팁들입니다. === 소스 공개 === 제 프로젝트에서 Git은 제가 저장 및 공개하고 싶은 파일들을 정확히 추적하여 줍니다. $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === 바뀐 것은 꼭 commit === Git에게 무엇을 추가, 삭제 및 파일이름을 바꾸었는지 일일히 알려주는 것은 귀찮은 짓일지도 모릅니다. 대신에 당신은 다음 명령어를 쓸 수있습니다: $ git add . $ git add -u Git은 현재 작업중인 디렉토리에 있는 파일들을 자동으로 살피며 자세한 사항들을 기록합니다. 위의 두번째 명령어 (git add -u) 대신에 'git commit -a'를 사용하여 그 명령어와 commit을 동시에 해낼 수 있습니다. *git help ignore*를 참고하여 어떠한 지정된 파일을 무시하는 방법을 알아보십시오. 위의 모든 것을 한 줄의 명령어로 실행할 수 있습니다. $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove *-z*와 *-0* 옵션은 파일이름이 이상한 문자를 포함하고 있을 때 생길 수 있는 여러가지 문제점들을 처리하여 줍니다. 이 옵션들은 무시된 파일들을 포함하여줌으로써 '-x'아니면 '-X'을 같이 써주어야 할 것입니다. === 내 commit이 너무 클 경우? === Commit을 한지 시간이 좀 많이 지난 상황입니까? 코딩을 너무 열심히 한 나머지 버전컨트롤하는 것을 깜빡했나요? 프로젝트에 여러가지 연관성없는 수정을 한 상태입니까? 걱정하지말고: $ git add -p 당신이 만든 모든 수정작업에 대하여 Git은 어떠한 것들이 바뀌였는지 코드로 보여주며 당신에게 다음에 실행할 commit에 부분적인 코드가 포함될 사항인지 물어볼 것입니다. "y"와 "n"를 이용하여 대답할 수 있습니다. 물론 이 대답을 당장하지 않아도 됩니다; "?"로 좀 더 알아보십시요. 모든 수정작업이 마음에 든다면: $ git commit 위의 간단한 명령어를 사용하여 부분적인 commit을 실행합니다. 이 상황에선 반드시 *-a*옵션을 생략하시길 바랍니다. 그렇지 않으면 Git은 모든 수정작업을 commit할 것입니다. 만약에 여러군데 다른 디렉토리에 많은 수정작업을 해놓았다면 어떻게 할까요? 수정된 사항을 하나씩 검토하는 작업은 정말 귀찮은 짓입니다. 이럴땐 *git add -i*를 사용합니다. 몇 번의 타이핑만으로도 특정 파일의 수정작업을 검토할 수 있게됩니다. 또는 *git commit \--interactive*를 사용하여 작업 중 자동으로 commit하는 방법도 있을 수 있습니다. === 인덱스: Git의 스테이징 구역 === 여태까지 Git의 유명한 기능인 'index'를 피해왔지만 이제 한 번 살펴본 시간이 온 것 같습니다. 인덱스는 임시적인 스테이징 구역 (번역주:책갈피처럼)으로 보면 됩니다. Git은 당신의 프로젝트와 프로젝트의 기록 사이에 데이터를 직접 옮기는 경우는 드뭅니다. 대신, Git은 인덱스에 파일을 쓰며 그리고 그 파일들을 마지막 목표지점에 카피하여 줍니다. 예를 들어 *commit -a*는 원래 투-스텝 과정을 거치는 하나의 명령어입니다. 첫번째로는 현 작업상황의 스냅샷을 찍어 모든 파일들을 인덱스하는 과정을 거칩니다. 두번째 과정에서는 방금 찍은 스냅샷을 영구적으로 보관하게 됩니다. *-a* 옵션을 쓰지않고 commit을 하는 것은 이 두번째 과정만 실행하는 일입니다. 그렇기에 *git add* 같은 명령어를 쓴 후에 commit을 하는 것이 당연한 이야기가 되겠지요. 대체적으로 인덱스에 관한 컨셉트는 무시하고 파일기록에서 직접적으로 쓰기와 읽기가 실행된다는 개념으로 이해하면 편합니다. 이런 경우에는 우린 인덱스를 제어하는 것 처럼 좀 더 세부한 제어를 하기 원할것입니다. 부분적인 스냅샷을 찍은 후 영구적으로 이 '부분스냅샷'을 보존하는 것이죠. === 대가리(HEAD)를 잃어버리지 않기 === HEAD 태그는 문서작업시 보이는 커서처럼 마지막 commit 포인트를 가르키는 포인터 역할을 합니다. Commit을 실행할 때마다 물론 HEAD도 같이 앞으로 움직이겠지요. 어떤 Git 명령어들은 수동으로 HEAD를 움직일 수 있게 해줍니다. 예를 들면: $ git reset HEAD~3 위 명령어를 사용한다면 HEAD를 commit을 3번 하기 전으로 옮깁니다. 이 후 모든 Git 명령어는 가지고 있던 파일은 현재상태로 그대로 두되 그 3번의 commit을 하지 않은 것으로 이해하죠. 그러나 어떻게 해야 다시 미래로 돌아갈 수 있을까요? 예전에 했던 commit들은 미래에 대해서 아무것도 모를텐데 말이지요. 원래의 HEAD의 SHA1을 가지고 있다면: $ git reset 1b6d 그러나 이 것을 어디에도 써두지 않았었더라도 걱정하지 마십시오: Git은 이런 경우를 대비해서 원래의 HEAD를 ORIG_HEAD로 어딘가에는 저장하여 둡니다. 그러고는 다음명령어를 사용하여 미래로 안전하게 돌아올 수 있지요: $ git reset ORIG_HEAD === HEAD-헌팅 === ORIG_HEAD로 돌아가는 것만으로는 충분하지 않을지도 모릅니다. 당신은 방금 엄청나게 큰 실수를 발견하였고 아주 오래전에 했던 commit으로 돌아가야 할지 모릅니다. 기본적으로 Git은 나뭇가지를 수동으로 삭제하였더라도 2주의 시간동안 commit을 무조건 저장하여 둡니다. 문제는 돌아가고 싶은 commit의 hash를 찾는 일입니다. '.git/objects'의 모든 hash 값을 조회하여 얻어걸릴 때까지 해보는 방법이 있긴합니다만, 여기 좀 더 쉬운 방법이 있습니다. Git은 모든 commit의 hash를 '.git/logs'에 저장해 둡니다. 하위 디렉토리 'refs'은 모든 나뭇가지의 활동기록을 저장하여두고 동시에 'HEAD'파일은 모든 hash 값을 저장하고 있습니다. 후자는 실수로 마구 건너 뛴 commit들의 hash도 찾을 수 있게 해줍니다. reflog 명령어는 당신이 사용하기 쉬운 유저인터페이스로 log파일들을 표현하여 줍니다. 다음 명령어를 사용하여 보십시오. $ git reflog hash를 reflog으로부터 자르고 붙여넣기 보다는: $ git checkout "@{10 minutes ago}" 아니면 5번 째 전에 방문했던 commit으로 돌아갈수도 있습니다: $ git checkout "@{5}" 좀 더 많은 정보는 *git help rev-parse*의 "재편집 구체화하기" 섹션을 참고하십시오. Commit의 2주의 생명을 연장하여 줄 수 있습니다. 예를 들어: $ git config gc.pruneexpire "30 days" 위 명령어는 30일이 지난 후에 지워진 commit들 역시 영구적으로 삭제된다는 의미입니다. 그러고는 *git gc*가 실행되지요. *git gc*가 자동실행되는 것을 꺼줄 수도 있습니다: $ git config gc.auto 0 이 경우에는 *git gc*를 수동적으로 실행시켜 commit들을 삭제할 수 있지요. === Git을 좀 더 발전시키는 방법 === 진정한 UNIX와 같이 Git의 디자인은 다른 프로그램들의 GUI, 웹 인터페이스와 같은 하위파트들과 호환이 됩니다. 어느 Git 명령어들은 유명인사의 어깨위에 서있는 것처럼 Git 그 자체가 스크립팅 언어로 사용될 수도 있습니다. 조금만 시간을 투자하면 Git은 당신의 선호에 더 알맞게 바꿀수 있습니다. 한 가지 트릭을 소개하자면 자주 사용할것 같은 명령어들을 짧게 만들어줄 수 있는 방법이 있습니다: $ git config --global alias.co checkout $ git config --global --get-regexp alias # 현재 설정한 명령어들의 '가명'을 표기해줍니다. alias.co checkout $ git co foo # 'git checkout foo'와 같은 것입니다. 또 다른 트릭은 현재 작업중인 나뭇가지의 이름을 작업표시창에 표시하여주는 명령어도 있습니다. $ git symbolic-ref HEAD 위 명령어는 현재 작업중인 나뭇가지 이름을 표기하여 줍니다. 실용적으로는 "refs/heads/"를 사용하지 않으며 잠재적으로 일어날 수 있는 에러들을 무시하여 줍니다: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- +contrib+ 하위 디렉토리는 유용한 Git 툴들이 저장되어있는 장소이기도 합니다. 시간이 지나면 이곳에 있는 툴들은 공식적으로 인정받아 고유명령어가 생기기도 하겠지요. Debian과 Ubuntu에서는 이 디렉토리는 +/usr/share/doc/git-core/contrib+에 위치하고 있습니다. 앞으로 +workdir/git-new-workdir+디렉토리에 방문할 일도 많을 것입니다. 시스템링크 기술을 통해서 이 스크립트는 원래의 저장소와 작업기록을 같이하는 새로운 작업 디렉토리를 생성하여 줍니다: $ git-new-workdir an/existing/repo new/directory 새롭게 생성된 디렉토리는 클론으로 봐도 무방하며 일반클론들과의 한가지 차이점은 어느 한 곳에서 작업을 하던 두 개의 디렉토리는 앞으로 계속 싱크를 진행하며 같은 기록을 가지게 된다는 것입니다. 즉, 병합, 밀어넣기, 당겨오기를 해줄 필요가 없어지는 것이지요. === 용감한 스턴트 === Git은 요즘 유저들이 데이터를 쉽게 지우지 못하도록 하고 있습니다. 그러나 몇가지의 상용적인 명령어를 통해서 이런 Git만의 방화벽 쯤은 쉽게 뚫어버릴 수 있지요. *Checkout*: Commit하지 않은 작업들은 checkout을 할 수없습니다. 방금작업한 모든 것들을 없던 일로하고 그래도 굳이 commit을 진행하고 싶다면: $ git checkout -f HEAD^ 반면에 checkout을 할 위치를 수동으로 설정하여 준다면 Git의 방화벽은 처음부터 작동하지 않을 것입니다. 설정해준 위치는 조용히 덮어씌우게 됩니다. 그러니, 이런 방식으로 checkout을 할 때에는 주의 하십시오. *Reset*: 리셋은 commit되지 않은 작업이 있으면 실행되지 않을 것입니다. 그래도 강제로 하고싶다면: $ git reset --hard 1b6d *Branch*: 방금한 작업을 잃어버릴 것같으면 Git은 나뭇가지가 지워지지 않게합니다. 그래도 하고싶다면: $ git branch -D dead_branch # -d 대신 -D 비슷한 방식으로, commit을 안한 작업이 있어서 move명령어를 통해서 덮어씌우기가 안될경우에는: $ git branch -M source target # -m 대신 -M 체크아웃과 리셋과는 다르게 위의 두 명령어는 데이터를 직접 삭제하진 않습니다. 모든 변경기록은 .git 하위 디렉토리에 남게되고 필요한 hash는 '.git/logs'에서 찾을 수 있습니다 (위의 "HEAD-헌팅" 섹션 참고). 기본설정상, 이 기록들은 2주 동안은 삭제되지 않습니다. *Clean*: 몇 git 명령어들은 추적되지 않은 파일들을 망쳐놓을까봐 실행이 안되는 경우가 종종 있습니다. 만약에 그 파일들이 삭제되도 된다는 확신이 선다면, 가차없이 다음 명령어를 사용하여 삭제하십시오: $ git clean -f -d 이 후에는 위 모든 명령어들은 다시 잘 실행되기 시작할 것입니다! === 원치않는 commit들을 방지하기 === 바보같은 실수들은 내 저장소를 망쳐놓곤 합니다. 그 중에서도 제일 무서운 것은 *git add*를 쓰지 않아서 작업해놓은 파일들을 잃어버리는 것이지요. 그나마 코드 뒤에 빈 공간을 마구 넣어놓는다던지 병합에서 일어날 수 있는 문제들을 해결해 놓지않는 것은 애교로 보입니다: 별로 문제가 되는 것들은 아니지만 남들이 볼 수 있는 공공저장소에서는 보여주기 싫습니다. _hook_ 을 사용하는 것과 같이 제가 바보같은 짓을 할 때마다 경고를 해주는 기능이 있다면 얼마나 좋을까요: $ cd .git/hooks $ cp pre-commit.sample pre-commit # Older Git versions: chmod +x pre-commit 이제는 아까 설명했던 애교스러운 실수들이 발견될 때 Git은 commit을 도중에 그만 둘것입니다. 이 가이드에서는 *pre-commit* 앞에 밑에 써놓은 코드를 넣음으로써 혹시 있을지도 모르는 바보같은 짓을 방지하였습니다. if git ls-files -o | grep '\.txt$'; then echo FAIL! Untracked .txt files. exit 1 fi 많은 git 작업들은 hook과 상호작용합니다; *git help hooks*를 참조하십시오. 우리는 Git over HTTP를 설명할때 *post-update* hook을 활성화시켰습니다. HEAD가 옮겨질때마다 같이 실행되지요. Git over HTTP 예제에서는 post-update 스크립트가 통신에 필요한 Git을 업데이트 했었습니다. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/history.txt��������������������������������������������������������������������0000644�0001750�0001750�00000034403�12666307504�015657� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== 기록공부 == 분산 관리 시스템을 택한 Git은 개발자들이 전 버전에서의 작업을 더 용이하게 할 수 있게 도와주었습니다. 그러나 프로그램의 과거를 들춰내려면 조심하세요: 당신이 소유하고 있는 파일들만 다시쓰기 하세요. 세계 각국의 나라들이 누가 어떤 잘못을 했는지 끝임없이 따지는 것처럼 만약 한 개발자가 당신이 가지고 있는 파일과 기록 (history)이 다른 파일들을 클론하여 갔을 때 병합할때 문제가 생길지도 모릅니다. 어떤 개발자들은 파일의 기록은 절대로 바뀌면 안되는 것이라고 믿고 있습니다. 또 어떤 개발자들은 수정 기록들이 깨끗하게 정리되어야 한다고 합니다. Git은 이렇게 다른 성향의 개발자들을 모두 포용할 수 있습니다. 클로닝, 나뭇가지, 병합과 같은 기능들과 같이 파일의 기록들을 바꾸는 것은 Git이 할 수있는 많은 기능들 중에 하나일 뿐입니다. 어떻게 영리하게 사용하는지는 당신에게 달려있죠. === 오류 수정합니다 === 방금 commit을 했는데, 그 commit 메세지를 바꾸고 싶다고요? 그렇다면: $ git commit --amend 위 명령어를 사용하면 마지막으로 한 commit의 메세지를 바꿀 수 있습니다. 파일을 더하는 것을 잊어버리고 commit을 했다고요? *git add*를 사용하고서 위의 명령어를 사용하세요. 마지막으로 했던 commit에 편집을 더 하고 싶으신가요? 그렇다면 편집 후에 다음 명령어를 쓰세요. $ git commit --amend -a === ... 더 있습니다 === 전에 보았던 문제가 10배 더 힘들었다고 생각합니다. 긴 시간동안 작업해서 많은 commit을 하였다고 가정합니다. 그러나 당신은 그 commit들의 난잡한 구성이 마음에 들지 않습니다. 그리고 어떤 commit 메세지들은 다시쓰고 싶습니다. 그렇다면: $ git rebase -i HEAD~10 위 명령어를 사용한다면 당신이 좋아하는 작업용 에디터에 지난 열 개의 commit이 출력될 것입니다. 샘플을 보자면: pick 5c6eb73 Added repo.or.cz link pick a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile 여기서는 오래된 commit이 'log' 명령어와 달리 새로운 commit보다 먼저 출력되어 나옵니다. 여기서는 5c6eb73 가 가장 오래된 commit이고 100834f이 가장 최근 commit 이죠. 그리고: Here, 5c6eb73 is the oldest commit, and 100834f is the newest. Then: - 한 줄을 지움으로써 commit을 삭제합니다. 'revert' 명령어와 같으나 기록에는 남지 않게 지웁니다. 이 전략은 마치 commit이 처음부터 존재하지 않던 것처럼 보여지게 해줍니다. - 행들을 재정렬하며 commit의 순서를 바꾸어 줍니다. - 'pick' 명령어 대신에 * `edit` 을 사용하여 개정시킬 commit을 마킹합니다. * `reword`를 사용하여 로그메세지를 바꿉니다. * `squash` 를 사용하여 전에 했던 commit과 합병합니다. * `fixup` 를 사용하여 전에 했던 commit과 합병 후 log 메세지를 삭제합니다. 예를들어, 두번째 행의 'pick'을 'squash' 명령어로 바꾸어 봅니다: pick 5c6eb73 Added repo.or.cz link squash a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile 저장 후 프로그램을 종료하면, Git은 a311a64를 5c6eb73로 병합시킵니다. *squash* (짓누르기)는 현 작업을 다음 commit으로 밀어붙어버린다고 생각하시면 됩니다. Git은 로그메세지들도 합친후 나중에 편집할 수 있게 해줍니다. *fixup* 명령어를 사용하면 이런 절차를 하지않아도 됩니다; 짓눌려진 로그메세지들은 간단히 삭제되기 때문입니다. *edit*을 이용하여 commit을 마킹해두었다면, Git은 같은 성향의 commit들 중에 가장 오래전에 했던 commit의 작업상태로 당신을 되돌려 보냅니다. 이 상태에서 아까 전 말했듯이 편집작업을 할 수도 있고, 그 상태에 맞는 새로운 commit을 만들수도 있습니다. 모든 수정작업이 만족스럽다면 다음 명령어를 사용해 앞으로 감기를 실행할 수 있습니다.: $ git rebase --continue Git은 다음 *edit*까지 아니면 아무런 *edit*이 없다면 현재 작업상태까지 commit을 반복실행 할것입니다. 새로운 평가기준 (rebase)을 포기할 수도 있습니다: $ git rebase --abort 그러니 commit을 부지런하게 자주하십시오: 나중에 rebase를 사용하여 정리할 수 있으니까요. === 로컬에서의 수정작업 === 어떤 프로젝트를 진행하고 있습니다. 당신의 컴퓨터에서 로컬 commit을 하다가 이제 공식적인 프로젝트 파일들과 동기화 해야합니다. 이런 절차는 서버의 파일에 올리기전에 거쳐야 할 과정이지요. 그러나 당신의 로컬 Git클론은 공식적인 파일기록와 개인으로 만든 파일기록이 뒤죽박죽 섞여있을 것입니다. 아무래도 공식적인 기록과 개인적인 기록이 분류되어 출력되면 기록을 확인하기가 쉽겠지요. 위에서 설명했듯이 *git rebase* 명령어가 이 작업을 해줄것입니다. *--onto* 플래그를 사용할 수도 있습니다. *git help rebase*를 확인해서 좀 더 자세한 예를 한번 봐보세요. Commit을 분류할 수 있다는 걸 알게될 것입니다. 나뭇가지를 재정리할 수도있죠. *rebase*는 유용한 명령어입니다. 여러가지 실험을 하기전에 *git clone*으로 복사본을 만들어두고 놀아보세요. === 기록 다시쓰기 === 가끔은 어떤 그룹사진에서 포토샵으로 몇 사람지우는 기능과 같은 명령어가 필요할지도 모릅니다. 스탈린식 스타일로 사람을 무자비하게 지우는 명령어 말입니다. 예를들어 이제 어떤 프로젝트를 풀 시간이 왔다고 가정합니다. 그러나 어떤 파일들은 다른 사람들이 보지 못하도록 하고싶습니다. 당신 크레딧카드 번호를 실수로 썻다던지 이런 실수를 한다면 더욱 더 그러고 싶겠지요. 예전 commit으로 파일을 부분적으로 복구하는 것이 가능하기 때문에 파일을 지우는 것으로는 부족합니다. 당신은 이 파일을 모든 commit으로 부터 없에야 할 것입니다: $ git filter-branch --tree-filter 'rm top/secret/file' HEAD *git help filter-branch*를 보세요. 여기서는 본 예시에 대해 설명하고 있고 더 빠른 방법을 제시하여 줄 것입니다. 대체적으로 *filter-branch*은 하나의 명령어만으로도 대량의 파일기록을 변화시킬 수 있을 것입니다. 그리고 +.git/refs/original+ 디렉토리는 이렇게 만든 변화가 오기 전의 기록을 보여줄 것입니다. *filter-branch* 명령어가 어떻게 작용했는지 확인할수도 있고, 이 디렉토리를 지운다면 더 많은 *filter-branch*명령어를 실행시킬 수 있습니다. 마지막으로, 나중에 새로운 버전에서 작업을 진행하고 싶다면 당신의 클론을 새로운 버전의 클론으로 바꾸시면 됩니다. === 기록 만들기 === [[makinghistory]] 프로젝트를 Git으로 옮겨오고 싶다고요? 다른 버전 관리 시스템에서 옮겨오는 것이라면, 어떤 개발자가 이미 프로젝트의 기록을 Git으로 옮겨오는 스크립트를 써두었을지도 모릅니다. 아니라면, 특정 포맷으로 텍스트를 읽어 Git 기록을 처음부터 작성하여 주는 *git fast-import* 명령어를 사용해서 확인해 보세요. 대체적으로 기록 스크립트는 한번에 간편하게 이 명령어를 사용해서 만들어졌을 것입니다. 예로, '/tmp/history' 같은 임시파일에 다음 텍스트를 붙여넣기 해보세요: ---------------------------------- commit refs/heads/master committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000 data <<EOT Initial commit. EOT M 100644 inline hello.c data <<EOT #include <stdio.h> int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800 data <<EOT Replace printf() with write(). EOT M 100644 inline hello.c data <<EOT #include <unistd.h> int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- 그리고 Git 저장소를 만들어보세요: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history 가장 최근 버전을 가져오고 싶다면: $ git checkout master . *git fast-export* 명령어는 아무 Git 저장소를 결과물이 사람들이 읽을 수 있는 포맷으로 바꾸어 주는 *git fast-import* 포맷으로 바꾸어 줍니다. 이 명령어들은 텍스트 파일들을 텍스트 파일 전용채널을 통해서 저장소로 밀어넣기 해줍니다. === 어디서부터 잘못되었을까? === 당신은 몇 달전에는 잘 작동되던 프로그램이 갑자기 안 된다는 것을 발견했습니다. 아놔! 이 버그는 어디서부터 생긴 것일까요? 개발을 하면서 테스팅을 종종했더라면 진작에 알아챘을텐데요. 이미 그러기엔 너무 늦었습니다. 그러나 commit이라도 자주했다는 가정하에 Git은 이러한 짐을 덜어줄 수 있습니다: $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git에서 한 프로젝트를 반 (good과 bad) 으로 나누어서 테스팅을 실행합니다. 그리고 아직도 버그가 발견된다면: $ git bisect bad 버그가 더이상 없다면 위 명령어에서 "bad"를 "good"으로 바꾸세요. Git은 good 버전과 bad 버전 사이로 당신을 데려갑니다. 물론 버그를 찾을 확률은 높아지지요. 몇 번이렇게 반복하다보면 결국엔 버그를 일으킨 commit을 찾아낼 수 있게 도와줄 것입니다. 버그찾기를 완료했다면 다시 처음 작업상태로 (이젠 버그가 없겠지요) 돌아가야 겠지요: $ git bisect reset 수동으로 테스팅하는 것보단, 다음 명령어로 테스팅을 자동화할 수 있습니다: $ git bisect run my_script Git 은 기존 대비할 스크립트에 약간의 변화를 주어서 이것이 좋은 변화인지 안 좋은 변화인지 체크합니다: 좋은 변화는 0으로 무시해야할 변화는 125로 안 좋은 변화는 1과 127사이의 번호로 테스팅을 종료합니다. 마이너스 숫자는 이분화 (bisect)를 강제종료하지요. 당신은 이것보다 더 많은 일을 할 수 있습니다. 도움말은 이분화를 시각화해주는 방법과, 이분화 기록을 다시보는 방법, 확인된 이상없는 변화들은 건너띄어 테스팅을 가속 시켜주는 기능들을 배우실 수 있습니다. === 누가 망가뜨렸을까? === 다른 버전 관리 시스템들과 같이 Git은 누군가를 탓할 수 있는 기능이 있습니다: $ git blame bug.c 이 명령어를 사용하면 누가 언제 마지막으로 코딩작업을 했는지 표시하여 줍니다. 다른 버전 관리 시스템들과는 달리 모든 작업은 오프라인에서 진행됩니다. === 나의 개인경험담 === 중앙 버전 관리 시스템에서는 파일기록 변경은 어려울 뿐만아니라 관리자만이 변경할 수 있습니다. 클론, 나뭇가지 만들기와 병합하기는 네트워크를 통해서만 할 수 있는 작업들입니다. 브라우징 기록보기, commit하기 역시 중앙 버전 관리 시스템에서는 네트워크를 통해야만 합니다. 어떤 시스템에서는 네트워크 연결이 되어야지만 자기 자신이 작업한 기록을 보거나 편집할 수 있습니다. 중앙 버전 관리 시스템은 개발자의 수가 늘어남에 비례해서 더 많은 네트워크 통신을 요구하기 때문에 오프라인에서 작업하는 것보다 비싸질 수 밖에 없습니다. 그리고 제일 중요한 것은 모든 개발자들이 고급명령어들을 적재적소에 쓰지 않는다면 모든 작업이 어느정도는 무조건 느릴 수 밖에 없다는 것입니다. 극적인 케이스에서는 아주 기본적인 명령어 만으로도 잘못하면 느려질 수 밖에 없습니다. 무거운 명령어를 써야하나면 일의 효율성은 나쁜영향을 받을 수 밖에 없습니다. 저는 직접 이런 상황들을 겪어보았습니다. Git은 제가 사용한 버전 관리 시스템 중에 제일 처음이었죠. 저는 Git의 여러기능들을 당연하다 생각하고 금방 적응하였습니다. 저는 당연히 다른 버전 관리 시스템들 역시 Git이 제공하는 기능들을 가지고 있을 것이라고 생각하였습니다: 버전 관리 시스템을 선택하는 것은 텍스트 에디터나 웹 브라우저를 선택하는 것과 같은 맥락일 것이라고 생각하였습니다. 제가 중앙 버전 관리 시스템을 처음 사용하게 되었을땐 완전 쇼킹이였죠. 불안정한 인터 연결은 Git을 사용할 때 중요치 않습니다. 그러나 이러한 인터넷연결은 로컬디스크에서 작업하는 것 만큼은 절대 될 수 없죠. 그리고 저는 어떤 명령어는 연결 딜레이를 고려함에 따라 쓰지 않게되는 걸 시간이 지나며 깨달았습니다. 이런 행동은 제가 정말 하고싶었던 작업을 완벽히 이행하지 못하게 하는 방해물이 되었죠. 어쩔 수 없이 느린 명령어를 사용할 때는 저의 작업효율에 치명타를 입히기 일쑤였죠. 네트워크 연결이 완료되길 기다리면서 이메일 확인 및 다른 문서작업을 하며 시간을 때웠습니다. 그러다가 원래하던 작업에 돌아가면 다시 무엇을 했었는지 기억을 해내는데 시간이 많이 허비된 경험이 허다했습니다. 인간은 환경변화에 적응을 할 수는 있으나 빠르진 않죠. 공유된는 비극도 존재했죠: 네트워크 상황이 원활하지 않을 것이라는 기대와 미래에 네트워크 딜레이를 줄이기위해 기울인 노력들은 오히려 트래픽을 더 잡아먹을 수가 있다는 것입니다. 모든 개발자들이 네트워크를 원활하게하는 노력을 할수록 오히려 해가 될 수있다는 것입니다. 이게 무슨 아이러니한 일입니까?�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/translate.txt������������������������������������������������������������������0000644�0001750�0001750�00000002331�12666307504�016146� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Appendix B: Translating This Guide == I recommend the following steps for translating this guide, so my scripts can quickly produce HTML and PDF versions, and all translations can live in the same repository. Clone the source, then create a directory corresponding to the target language's IETF tag: see http://www.w3.org/International/articles/language-tags/Overview.en.php[the W3C article on internationalization]. For example, English is "en" and Japanese is "ja". In the new directory, and translate the +txt+ files from the "en" subdirectory. For instance, to translate the guide into http://en.wikipedia.org/wiki/Klingon_language[Klingon], you might type: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" is the IETF language code for Klingon. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Translate the file. and so on for each text file. Edit the Makefile and add the language code to the `TRANSLATIONS` variable. You can now review your work incrementally: $ make tlh $ firefox book-tlh/index.html Commit your changes often, then let me know when they're ready. GitHub has an interface that facilitates this: fork the "gitmagic" project, push your changes, then ask me to merge. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/intro.txt����������������������������������������������������������������������0000644�0001750�0001750�00000022520�12666307504�015306� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== 도입부 == 저는 비유법을 사용하여 버전 관리 시스템에 대하여 설명해보려 합니다. http://en.wikipedia.org/wiki/Revision_control 를 방문하셔서 덜 정신나간 버전의 설명을 보시길 권장합니다. === 일하는 것은 곧 노는 것 === 저는 거의 평생을 컴퓨터게임을 하며 지냈습니다. 하지만 어른이 되서야 버전 관리 시스템을 사용하기 시작했지요. 이런 건 저 혼자가 아닐 것이라 생각합니다. 그럼으로써 Git을 게임에 비유하며 설명하는 것이 Git을 이해하는 데 도움이 될 것이라 생각합니다. 코드나 문서를 편집하는 작업이 게임을 하는 것과 같다고 생각해보세요. 편집을 마친 후에는 세이브하고 싶겠지요. 그렇게 하기 위해서는 당신의 믿음직한 에디터에서 '세이브' 버튼을 누르면 될 것입니다. 그러나 이러한 행동은 예전 세이브를 덮어쓰는 결과를 초래하죠. 세이브 슬롯이 한 개밖에 없는 옛날 구형 게임을 생각하면 됩니다. 다시 말하자면, 세이브를 할 수는 있지만, 당신은 예전 세이브포인트로 돌아갈 수 없는 것 입니다. 이건 참...게임을 아주 재미있는 순간에 세이브를 해 놓았는데 돌아갈 수 없다는 것이죠. 더 나쁜 상황으로는, 당신의 세이브는 더 이상 이길 수없는 상태에 되어 있을 수도 있습니다. 그럴 경우에는 아주 처음부터 다시 시작해야 되겠지요. === 버전 관리 === 편집 시, '다른 이름으로 저장' 기능 및 사본을 다른 디렉토리에 만들어 놓는 방법 등을 이용해 오래된 버전을 보존할 수는 있습니다. 용량을 효율적으로 사용하기 위해서 압축을 할 수도 있죠. 이것은 참 원시적인 버전 컨트롤 방법입니다. 컴퓨터게임은 이런 과정에서 발전해 나간지 오래되었지요. 요즘 게임들은 여러개의 세이브 슬롯을 잘 활용하고 있습니다. 이 문제를 좀 더 꼬아서 보죠. 어떤 프로젝트나 웹사이트를 구성하는 소스코드와 같이 여러개의 파일이 하나로 묶여있다고 가정합시다. 현 버전의 프로젝트/웹사이트를 세이브하고 싶다면 모든 디렉토리를 기록해야 한다는 번거로움이 있지요. 일일이 많은 버전들을 관리한다는 것은 그리 효율적이지 않을 겁니다. 어떤 컴퓨터게임들은 정말로 모든 디렉토리를 각개 관리하는 형식으로 게임을 세이브하기도 합니다. 이런 게임들은 이런 불필요하게 세부적인 사항들을 게이머들이 보지 못 하게 하고 간편한 인터페이스를 통해 게이머들이 세이브파일들을 관리할 수 있게 해둡니다. 버전 관리 시스템은 이런 컨셉과 그리 다르지 않습니다. 버전 관리 시스템들은 디렉토리 등을 관리하기에 편한 인터페이스로 구성되어 있습니다. 원하는 만큼 세이브 및 불러오기를 실행할 수 있습니다. 컴퓨터게임들과는 다르게 용량을 효율적으로 사용하는 데에는 탁월한 성능을 보여주죠. 대부분의 케이스는 소수의 파일들만 살짝 편집을 하게되죠. 디렉토리 전체를 세이브하는 것 보다는 버전과 버전사이의 차이를 세이브하는 것이 용량을 효율적으로 쓰는 버전 컨트롤의 비밀입니다. === 분산 제어 === 어려운 컴퓨터 게임을 한다고 생각해보세요. 너무 어렵기 때문에 전 세계의 프로게이머들이 팀을 구성해 이 게임을 끝내보겠다고 합니다. 게임을 빨리 끝내는 것에 초점을 두는 스피드런이 현실적인 예 이지요: 각기 다른 특기를 가지고 있는 게이머들이 각자 자신있는 부분에서 활약함으로써 성공적인 결과를 만들어내는 것을 예로 들어봅니다. 어떻게 시스템을 구축해 두어야 게이머들이 서로의 세이브파일들을 올리거나 이어 받을 수 있을까요? 예전에 게임들은 중앙 집중식 버전 관리 시스템을 사용하였습니다. 한 개의 서버가 모든 게임 세이브파일을 저장했었지요. 그 서버외에는 아무 것도 그 세이브파일들을 관리할 수 있지 않았습니다. 풀어 말하면, 게이머들은 각자의 컴퓨터에 몇 개의 세이브파일들을 가지고 있었고, 게임을 진행하고 싶을 때에는, 파일들이 저장되어있는 서버에서 다운로드 받은 후, 게임을 좀 하다가, 다시 다른 게이머들이 진행할 수 있게 그 서버에 업로드 해 놓아야 합니다. 만약에 어떠한 이유에서라도 한 게이머가 예전에 세이브 해두었던 파일을 불러오고 싶다면 어떻게 될까요? 현재 세이브 시점은 아무리 잘 해도 이길 수 없는 상태로 저장이 되어있을지도 모르고, 게이머들은 현 시점보다 전으로 돌아가서 아까 못 구했던 강력한 아이템을 얻을 수 있는 시점으로 돌아가고 싶을지도 모릅니다. 그런게 아니라면 그들은 아마도 세이브파일 두 개를 비교하여 한 특정 게이머가 얼마나 진행을 해두었는지 알고 싶어할지도 모릅니다. 예전 세이브 파일을 불러오고 싶은 이유는 여러가지일 수 있습니다, 그러나 방법은 한 가지일 수 밖에 없지요. 중앙서버에서 불러오는 방법 말입니다. 더 많은 세이브파일을 원할 수록 서버와의 통신이 더 잦아질 수 밖에 없지요. 새로운 세대의 버전 관리 시스템들은 (Git을 포함하여), 분산 제어를 기본으로 합니다. 예전의 중앙관리 방식의 보편화된 방식이라고 생각하면 되지요. 한 게이머가 서버로부터 세이브파일을 받는다면 하나만 받게되는 것이 아니라 모든 세이브파일 받게 되는 겁니다. 중앙서버를 각자의 컴퓨터에 미러링한다고 보시면 됩니다. 처음에는 시간이 많이 걸릴 수 있습니다. 특히 그 세이브파일이 아주 긴 역사를 가지고 있다면 말이지요. 그러나 이 것은 길게보면 아주 효율적인 방법입니다. 이 방법을 통해 즉시 이득을 볼 수있는 점을 따진다면, 예전 세이브파일을 원할 때 중앙서버와 교신을 하지 않아도 된다는 점이지요. === 멍청한 미신 === 분산 제어 시스템에 대한 보편적인 오해가 있다면, 이 시스템은 공식적인 중앙 저장소가 필요한 프로젝트에는 적합하지 않다고 생각하는 것입니다. 이 것은 말도 안되는 오해이지요. 이 오해는 누군가의 사진을 찍는다는 것은 그 피사체의 영혼을 빨아온다는 말도 안 되는 논리와 같습니다. 다시 말하면, 중앙 저장소의 파일을 사본하는 것이 중앙 저장소의 중요성을 훼손한다는 것이 아닙니다. 중앙 버전 관리 시스템이 할 수 있는 모든 일들은 잘 짜여진 분산 관리 시스템이 더 잘 할수 있다는 것을 알아야 합니다. 네트워크상의 자원들은 기본적으로 로컬상의 자원들보다 비경제적일 수 밖에 없습니다. 나중에도 말씀드리겠지만 분산 제어 시스템도 문제점이 없는 시스템은 아닙니다. 그러나 주먹구구식의 생각으로 중앙 관리 시스템과 분산 관리 시스템을 비교하는 일은 없어야 할 것입니다. 다음 인용문이 이 것을 대변해 줍니다. 규모가 작은 프로젝트는 어떤 시스템으로부터 부분적인 특성만이 필요할 지 모르지만, 규모가 작은 프로젝트를 잘못 스케일링하는 시스템은 마치 로마 숫자를 이용해 작은 숫자들의 계산을 실행하는 것과 같다. 더욱이 당신의 프로젝트는 당신이 생각했던 것보다 더 큰 일이 될지도 모릅니다. 처음부터 Git을 사용한다는 것은 아무리 병뚜껑을 여는 데만 쓴다하여도 스위스아미나이프를 들고 다니는 것과 같은 것입니다. 드라이버가 필요할 경우 당신은 병따개를 들고다니지 않았다는 사실에 안도의 한 숨을 쉴 수 있을 것 입니다. === 결합의 오류 === 이 주제를 설명하기 위해서는 컴퓨터 게임에 비유하는 것은 더 이상 적합하지 않을 수 있습니다. 대신에 여기서는 문서편집에 비유해서 설명드리도록 하죠. 앨리스는 파일 편집 중 첫 줄에 새로운 줄을 추가하고, 밥은 그 파일의 마지막에 한 줄을 넣는다고 가정합니다. 그들은 편집된 파일을 업로드 합니다. 대부분의 시스템은 자동으로 두 사람이 한 편집을 받아들이고 병합할 것입니다. 결과적으로는 앨리스와 밥 두 사람의 편집이 적용될 것입니다. 자 이제 앨리스와 밥이 같은 부분에서 서로 다른 편집을 한다고 가정해 봅니다. 인간의 직접적인 개입없이는 불가능 하겠지요. 누가 편집을하던 두번째로 편집하는 사람은 merge conflict라는 메세지를 볼 수밖에 없겠지요. 한 사람만의 편집을 선택하거나 두 사람의 편집과는 다른 세번째 편집을 생각해 봐야 할 겁니다. 더 복잡한 상황이 일어날 수 있습니다. 버전 관리 시스템은 간단한 상황들을 알아서 해결해 줍니다. 어려운 상황은 인간의 손에 맡기지요. 시스템의 행동은 대체적으로 조정가능합니다. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/ko/clone.txt����������������������������������������������������������������������0000644�0001750�0001750�00000034237�12666307504�015263� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== 클론 만들기 == 구식의 버전 관리 시스템에서는 체크아웃 명령어가 파일들을 가져오는 보편적인 방법이었습니다. 저장된 포인트로 부터 많은 파일들을 불러올 수 있죠. Git을 포함한 다른 분산 제어 시스템에서는 클론만들기를 보편적인 방법으로 채택하고 있습니다. 파일을 얻기위해서는, 원하는 파일들이 저장되어있는 저장소에서 '클론'을 만들어야 합니다. 즉, 중앙 관리 서버를 미러링해오는 것과 같은 이치라고 설명할 수 있습니다. 주 저장소가 할 수 있는 모든 것들을 당신이 이제 할 수 있는 것이죠. === 컴퓨터 동기화 === 기본적인 동기화 및 백업을 할 때 tarball을 만드는 것과 *rsync*명령어를 사용하는 것은 어느정도 견딜 수 있습니다. 그러나 저는 가끔씩 노트북에서 편집을 할 때도 있고, 데스크탑에서 할 때도 있는데, 이 두 개의 컴퓨터는 그리많은 대화를 나누지 않을지도 모릅니다. 한 컴퓨터에서 Git 저장소를 초기화하고 파일들을 commit함으로써 이 문제를 해결할 수 있습니다. 그 후 다른 컴퓨터에서: $ git clone other.computer:/path/to/files 위 명령어를 이용해서 두 번째 파일/Git 저장소 사본을 만들 수 있습니다. 그 다음부터는, $ git commit -a $ git pull other.computer:/path/to/files HEAD 을 이용하여 현재 사용중인 컴퓨터로 다른 컴퓨터에 있는 파일들을 '당겨올 (pull)' 수 있습니다. 만약에 같은 파일에 대해서 전후가 맞지않는 편집을 했을 경우, Git은 당신에게 에러메세지로 먼저 이 모순을 해결 후 commit을 할 것을 알려줄 것입니다. === 고전적인 소스 관리 === 우선 Git 저장소를 초기화 해줍니다: $ git init $ git add . $ git commit -m "Initial commit" 그리고 중앙 서버에서, 아무 디렉토리에서나 간단한 저장소를 초기화 해줍니다: $ mkdir proj.git $ cd proj.git $ git --bare init $ touch proj.git/git-daemon-export-ok 필요하다면 Git daemon을 실행합니다: $ git daemon --detach # it may already be running Git 호스팅 서비스를 한다면 우선 빈 Git 저장소를 만들어야 합니다. 대부분 웹페이지에서 어떠한 문서를 작성하곤 하죠. 다음 명령어를 사용해 당신의 프로젝트를 중앙서버로 '밀어넣기 (push)' 할 수 있습니다: $ git push central.server/path/to/proj.git HEAD 소스를 확인하고 싶을 때에 개발자는 다음 명령어를 사용합니다: $ git clone central.server/path/to/proj.git 편집이 끝난 후에 개발자는 다음명령어를 사용해 로컬드라이브에 각종 바뀐 사항들을 저장을 합니다: $ git commit -a 가장 최신 버전으로 로컬파일들을 갱신하려면: $ git pull 결합상의 곤란한 점들은 다음 commit 명령어를 사용하면 대부분 해결 될 것입니다: $ git commit -a 로컬에서 바뀐 사항들을 중앙 저장소로 저장하기 위해서는: $ git push 주 서버가 다른 개발자들로 인하여 새로운 변경사항이 생겼을 경우에는, '밀어넣기 (Push)'는 실패할 것입니다. 그렇다면 그 개발자는 최신 버전을 다시 당겨서 (pull) 결합후 다시 밀어넣기를 시도해야 하겠지요. 모든 개발자들은 push와 pull에 관한 SSH 접근권이 있어야합니다. 그러나 소스는 모든 이들에게 개방된 것으로써 다음 명령어를 이용하면 조회가 가능합니다: $ git clone git://central.server/path/to/proj.git Git 프로토콜은 HTTP와 비슷합니다: 증명서가 존재하지 않죠. 그래서 아무나 프로젝트를 조회할 수 있는겁니다. 그런 이유에서 '밀어넣기 (push)'는 Git 프로토콜로는 할 수없게 디폴트 설정이 되어있지요. === 숨겨진 소스 === 개방되어 있지않은 소스의 프로젝트를 진행할 때에는 터치 (Touch) 명령어를 생략합니다. 그리고 'git-daemong-export-ok'라는 이름의 파일을 만들지 않도록 주의합니다. 이렇게하면 git 프로토콜을 사용해서 원치않는 사람들이 당신의 저장소를 조회할 수 있는 일은 없을 것입니다; 이제는 SSH 접근권이 있는 사람들만 조회할 수 있게 될겁니다. 당신의 모든 저장소가 개방되지 않은 경우에는 git daemon명령어는 필요없겠지요. 모든 저장소는 SSH 접근방식을 필요로 할 테니까요. === 헐벗은 저장소 === 이 괴상한 이름의 저장소 (bare repository)는 현재 작업중인 디렉토리가 아니기에 이렇게 이름이 붙여졌습니다; 이 저장소는 하위 '.git' 디렉토리에서 숨겨진 파일들만을 저장하는 저장소입니다. 풀어 설명하면, 이 저장소는 현 프로젝트의 과거를 관리하기만 하고, 아무런 버전도 저장하지 않는 저장소입니다. 헐벗은 저장소는 버전 관리 중앙 서버와 비슷한 기능을 담당하고 있고 당신의 프로젝트가 저장되어 있는 집과같은 기능을 담당하고 있습니다. 개발자들은 이 곳에서 부터 클론을 만들 수 있고, 편집한 내용을 '밀어넣기 (Push)' 할 수 있습니다. 보편적으로 헐벗은 저장소는 서버에서 상주하고 있다가 데이터를 퍼트리는 역할을 맡고있습니다. 개발은 만들어진 클론에서 이루어짐으로써, 워킹 디렉토리없이 서버내에서 보호받는 저장소 역할을 할 수 있습니다. 많은 Git 명령어들은 'GIT_DIR' 환경 변수가 저장소로 path가 세팅되어 있지 않는 한 이 헐벗은 저장소에 인식되지 않을 것입니다. '--bare' 옵션을 이용한다면 모를까. === 밀어넣기 (push) vs. 당기기 (pull) === 당기기 (pull)에 의존하는 대신에 왜 제가 밀어넣기 (push)를 소개했을까요? 먼저, 당기기 (pull)는 아까 소개드린 헐벗은 저장소에서는 실행이 되지 않는 명령어입니다: 물론 나중에 소개할 물어오기 (fetch)라는 명령어로 같은 일을 할 수 있지만요. 그러나 헐벗은 것 말고 보통 일반적인 저장소를 중앙 서버에 저장해 놓는다고 해도, 당기기 (pull)는 번거로울 수 밖에 없습니다. 서버에 로그인을 해야 될 것이고 그런 후에야 당기기 (pull)을 사용해야 하다는 말이지요. 파이어월이 이런 작업을 방해할 수도 있습니다. 그리고 쉘 접근 권한이 없다면 중앙 서버에 접속이나 가능할런지요? 그러나 이러한 특수상황들이 아니라면 밀어넣기 (push)를 사용하실 것을 강추합니다. 목적지가 현재 작업중인 디렉토리가 있을 경우에는 굉장히 햇갈릴 수 있기 때문입니다. 줄여서, Git을 배울 때에는, 헐벗은 저장소일 경우에는 밀어넣기 (push) 아니면 당기기 (pull)을 사용합시다. === 프로젝트 포크질 (forking) 하기 === 현재 프로젝트가 진행되고 있는 방식이 마음에 안 드신 다고요? 당신이 좀 더 잘할 수 있다고 생각하세요? 그렇다면 당신 서버에서: $ git clone git://main.server/path/to/files 이 명령어를 쓴 후에, 다른 사람들에게 당신이 포크질 (fork)을 한 프로젝트에 대해 알리세요. 이후 아무때나 원래 프로젝트 파일에서 다음 명령어를 씀으로써 어떠한 변화가 있었다면 포크질 해놓은 프로젝트로 병합을 실행할 수 있습니다: $ git pull === 궁극의 백업 === 아무도 건들 수 없고 지리적으로 다양한 곳에 저장해놓고 싶은 기록 보관소를 소유하고 싶다고요? 만약 당신의 프로젝트에 많은 개발자들이 참여한다면 아무 것도 하지 마십시오. 클론을 만드신다면 그 클론 자체가 아주 효율적인 프로젝트 백업이 될 것 입니다. 현 상태의 프로젝트 뿐만이 아니라, 그 프로젝트의 모든 과거 버전까지 말이죠. 만약이라도 어떤 개발자 분의 클론이 훼손 된다면 암호화 된 hashing 덕에 다른 모든 개발자들이 프로젝트 훼손여부에 관해 알 수 있게 될 것입니다. 만약 당신의 프로젝트에 그리 많은 개발자들이 참여하지 않는다면, 최대한 많은 서버를 확보해서 클론을 만들어 놓으십시오. 편집증이 걸린 개발자들은 언제나 프로젝트 HEAD의 20-바이트 SHA1 hash를 어딘가에는 안전하게 모셔놓죠. 안전하다는 말이 사적인 공간에 저장해놓는다는 말은 아닙니다. 예를 들면, 어떤 신문에 기사를 개제하는 것도 안전한 기록보관의 한 방법이지요. 그 정보를 훼손하고자하는 작자들이 세상에 발간된 모든 신문 카피를 바꿀 수는 없기 때문입니다. === 광속의 멀티테스킹 === 만약에 어떠한 프로젝트의 여러군데를 동시에 작업하고 싶으실 때에는 우선 프로젝트를 한 번 commit 한 후 다음 명령어를 사용합니다: $ git clone . /some/new/directory http://en.wikipedia.org/wiki/Hard_link[hardlinking] 덕분에 클론들은 적은 시간과 공간을 이용해 백업으로 존재해줄 수 있습니다. 이렇게 하면 두개의 독립적인 구간에서 작업을 진행할 수 있습니다. 예로, 한 클론이 컴파일 중 다른 클론에서 작업을 진행하고 있을 수 있습니다. 그리고 다른 클론으로 부터 아무 때나 commit과 당기기 (pull)도 사용할 수 있습니다. $ git pull /the/other/clone HEAD === 게릴라 버전 관리 === 당신은 현재 다른 버전 관리 시스템을 사용하고 있지만, Git을 그리워하고 있진 않습니까? 그렇다면 현재 작업중인 디렉토리에서 Git을 초기화 시켜주십시오: $ git init $ git add . $ git commit -m "Initial commit" 그리고 클론을 만들고: $ git clone . /some/new/directory 이제 방금 클론 된 디렉토리에서 작업을 진행하시면 됩니다. 가끔은 다른 개발자 분들과 동기화하고 싶으시겠죠. 그 개발자분들은 아직 Git을 사용하고 있지 않아도 우리 Git에서는: $ git add . $ git commit -m "Sync with everyone else" 그리고는 새로운 디렉토리에서: $ git commit -a -m "Description of my changes" $ git pull 다른 분들에게 당신의 작업을 공유하는 일은 그 쪽 분들이 쓰시는 버전 관리 시스템에 따라 다릅니다. 새로운 디렉토리는 당신이 작업한 파일들이 포함되어 있죠. 위의 명령어를 쓰신 후에 다른 버전 관리 프로그램에서 쓰는 명령어를 통해서 그들의 중앙 서버에 업로드 하실 수 있습니다. Subversion은 가장좋은 중앙 버전 관리식 시스템으로써 개발자들 사이에서 애용되고 있습니다. Git에서 *git svn*을 사용해서 위에서 언급한 일들은 Subversion 저장소를 대상으로 행할 수 있습니다.http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[Git 프로젝트를 Subversion 저장소로 보내기]. === Mercurial === Mercurial 역시 비슷한 버전 관리 시스템으로써 Git과 쉽게 연동될 수 있습니다. 'hg-git'플러그인을 통해서 Mercurial 유저들은 Git 저장소에 쉽게 밀어넣기 (push)와 당기기 (pull)을 사용할 수 있죠. Git으로 'hg-git'을 구하는 방법: $ git clone git://github.com/schacon/hg-git.git Mercurial로 'hg-git'을 구하는 방법: $ hg clone http://bitbucket.org/durin42/hg-git/ 하지만 Git에 이 것과 비슷한 플러그인이 있는지는 모르겠다. 그렇기 때문에 Mercurial보다는 Git을 주 저장소를 쓰길 선호한다. Mercurial로 프로젝트를 진행할 경우에는 대부분의 케이스에 한 자원봉사 개발자가 Git 저장소를 관리하는 업무를 떠 맡곤 합니다. 그러나 Git으로 Mercurial 프로젝트를 진행할 경우에는 'hg-git'플러그인의 도움으로 그러한 번거로움이 필요없을 것입니다. 빈 저장소를 이용해서 Mercurial 저장소를 Git 저장소로 바꿀 수 있으나, 'hg-fast-export.sh'스크립트를 사용해 더 쉽게 이 작업을 끝낼 수 있습니다. 다음 저장소에서 이 스크립트를 구할 수 있습니다: $ git clone git://repo.or.cz/fast-export.git 빈 저장소에서 한 번 바꿔봅시다: $ git init $ hg-fast-export.sh -r /hg/repo 위 명령어는 스크립트를 '$PATH'에 넣은 후에 실행합니다. === Bazaar === Bazaar는 Git과 Mercurial 다음으로 많이 알려진 버전 관리 시스템 입니다. Bazaar는 작업 수정을 하기 용이하게 디자인 되어있지요; 개발자들은 과거의 실수에서 배우고 무시해도 될만한 에러에서 자유롭습니다. 그리고 Bazaar를 사용하는 개발자들은 다른 버전 관리 시스템들에 관해 굉장히 개방적인 분들 일겁니다. 'bzr-git'플러그인은 Bazaar 이용자들이 Git 저장소를 통해 작업할 수 있도록 해줍니다. 'tailor' 프로그램은 Bazaar 저장소를 Git 저장소로 바꿔줍니다. 'bzr-fast-export'도 한 번 검색해보세요. === 내가 Git을 사용하는 이유 === 제가 Git을 처음에 사용했던 이유는 제가 듣기에 Git은 Linux kernel source 관리에 용이하다고 들었기 때문입니다. Git을 사용한 이후로는 다른 버전 관리 시스템으로 바꿔야겠다는 생각은 들지도 않았지요. Git은 저에게 유용한 도움이 되었으나, Git도 완벽한 플랫폼은 아닙니다. 저는 Linux를 주로 이용하기 때문에 다른 플랫폼과의 문제는 생략하겠습니다. 그리고 저는 C, bash scripts, Python을 이용하는 사람이고 빠른 프로그램 시간에 목숨을 거는 사람 중 하나입니다. Git이 어떻게 좀 더 발전할 수 있을지 Git과 비슷한 프로그램도 짜보기도 했지만 학교 프로젝트 정도로만 썻었을 뿐입니다. 그러나 제 프로젝트를 완료하더라도 저는 Git을 계속 이용했을 겁니다. 제 프로그램을 써도 별로 투자한 것에 비해 얻을 것이 적어보였기 때문이지요. 자연스레 여러분들이 필요로하고 원하는 프로그램은 계속해서 바뀝니다. 그러나 Git과는 그럴 가능성이 매우 적지요.�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/it/�������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12666307504�013414� 5����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/it/drawbacks.txt������������������������������������������������������������������0000644�0001750�0001750�00000020031�12666307504�016112� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Appendice A: Le lacune di Git == Git presenta qualche problema che ho nascosto sotto il tappeto. Alcuni possono essere facilmente risolti con script e 'hook', altri richiedono di riorganizzare e ridefinire il progetto, e per le poche rimanenti seccature non vi rimarrà che attendere. O meglio ancora, contribuire con il vostro aiuto! === Le debolezze di SHA1 === Con il tempo, gli specialisti in crittografia continuano a scoprire debolezze di SHA1. È già possibile trovare collisioni hash (cioè sequenze di byte che risultano nello stesso codice hash), dati sufficienti mezzi. Fra qualche anno anche un normale PC potrebbe avere abbastanza potenza di calcolo per corrompere in maniera non rilevabile un deposito Git. Auspicabilmente Git sarà migrato verso un migliore sistema di funzioni hash prima che ulteriore ricerca distruggerà lo standard SHA1. === Microsoft Windows === Git per Microsoft Windows può essere piuttosto ingombrante: - http://cygwin.com/[Cygwin] è un ambiente di emulazione di Linux per Windows che contiene http://cygwin.com/packages/git/[una versione di Git per Windows]. - http://code.google.com/p/msysgit/[Git per MSys] è un alternativa che richiede meno risorse, anche se alcuni comandi necessitano ancora di essere migliorati. === File senza relazione === Se il vostro progetto è molto grande e contiene molti file scorrelati che tendono a cambiare spesso, Git può essere in svantaggio rispetto ad altri sistemi perché file singoli non sono tenuti sotto controllo. Git tiene sotto controllo l'intero progetto, che normalmente è una strategia vantaggiosa. Una soluzione è di suddividere il vostro progetto in pezzi, ognuno consistente di gruppi di file correlati. Usate *git submodule* se volete poi comunque mantenere tutto in un deposito unico. === Chi modifica cosa? === Certi sistemi di controllo di versione vi obbligano a marcare esplicitamente un file prima di poterlo modificare. Mentre questo è particolarmente fastidioso perché implica comunicazioni addizionali con un server centrale, ha comunque due benefici: 1. Il calcolo delle differenze è rapido, perché solo i file marcati devono essere esaminati. 2. Ognuno può sapere chi sta lavorando su un file chiedendo al server centrale chi l'ha marcato per modifiche. Con qualche script appropriato, potete ottenere la stessa cosa con Git. Questo richiede cooperazione dagli altri programmatori, i quali devono eseguire script particolari prima di modificare un file. === La storia di un file === Perché Git registra modifiche in maniera globale al progetto, la ricostruzione della storia di un singolo file richiede più lavoro che in altri sistemi di controllo di versioni che si occupano di file individuali. Questo sovrappiù è generalmente trascurabile e ne vale la pena visto che permette altre operazioni di incredibile efficienza. Per esempio, `git checkout` è più rapido che `cp -a`, e una differenza di versione globale al progetto si comprime meglio che una collezione di differenze di file individuali. === Il clone iniziale === Creare un clone è più costoso che fare un checkout in altri sistemi di controllo di versione se il progetto ha una storia lunga. Il costo iniziale è un buon investimento, visto che operazioni future saranno più rapide e offline. Tuttavia, in alcune situazioni può essere preferibile creare un clone superficiale utilizzando l'opzione `--depth`. Questo è più rapido, ma il clone risultante ha funzionalità limitate. === Progetti volatili === Git è stato scritto per essere rapido rispetto alla dimensione dei cambiamenti. Normalmente si tende a fare piccole modifiche da una versione all'altra. La correzione di un bug in una linea qui, una nuova funzionalità là, commenti corretti, e così via. Ma se i vostri file cambiano radicalmente in revisioni successive, ad ogni commit la vostra storia crescerà necessariamente proporzionalmente alle dimensioni dell'intero progetto. Non c'è niente che nessun sistema di controllo di versione possa fare per evitare questo, ma gli utilizzatori di Git ne soffriranno di più perché ogni clone contiene normalmente la storia completa. Bisogna cercare la ragione per cui questi cambiamenti sono così grandi. Magari bisogna cambiare il formato dei file. Modifiche minori dovrebbero causare solo cambiamenti minori in solo pochi file. Magari un database o un sistema d'archivio sono invece una soluzione più adatta invece di un sistema di controllo di versione. Per esempio, un sistema di controllo di versione potrebbe non essere adatto per gestire fotografie prese periodicamente da una webcam. Se i file devono essere cambiare radicalmente e se devono essere gestite in versioni, una possibilità è di usare Git in maniera centralizzata. È possibile creare cloni superficiali che contengono solo una parte minore o addirittura inesistente della storia del progetto. Naturalmente in questo caso molti strumenti di Git non saranno più a disposizione, e correzioni dovranno essere fornite sotto forma di patch. Questo va probabilmente bene, visto che non sembrerebbe doverci essere nessun motivo per mantenere la storia di file ampiamente instabili. Un altro esempio è un progetto che dipende da un firmware che consiste in un enorme file binario. La storia di questo firmware non interessa agli utilizzatori, e gli aggiornamenti non sono molto compressibili, il che significa che le revisioni del firmware inflazionano inutilmente il deposito. In questo caso il codice sorgente dovrebbe essere salvato in un deposito Git, mentre i file binari dovrebbero essere tenuti separatamente. Per rendere la vita più facile, si potrebbe distribuire uno script che usa Git per clonare il codice sorgente, e rsync o un Git superficiale per il firmware. === Contatore globale === Alcuni sistemi di controllo di versione centralizzati mantengono un numero intero che aumenta quando un nuovo commit è accettato. Git fa riferimento ai cambiamenti tramite il loro codice hash, un metodo migliore in molte circostanze. Alcune persone vorrebbero però avere accesso a questo contatore. Fortunatamente è facile scrivere uno script che fa in maniera di aumentare un contatore nel deposito Git centrale ad ogni aggiornamento, magari grazie ad una tag associata con un hash dell'ultimo commit. Ogni clone potrebbe mantenere un tale contatore, ma questo sarebbe probabilmente inutile, visto che solo il contatore del deposito centrale è interessante per gli utenti. === Sottocartelle vuote === Sottocartelle vuote non possono essere gestite. Create dei file segnaposto per rimediare a questo problema. Queste limitazioni non sono dovute a come Git è concepito, ma piuttosto a come è correntemente implementato. Con un po' di fortuna, se abbastanza utilizzatori lo richiedono, questa funzionalità potrebbe essere implementata. === Commit iniziale === In informatico tipico conta a partire da 0, invece che da 1. Sfortunatamente, rispetto ai commit, Git non aderisce a questa convenzione. Molti comandi non funzionano prima del primo commit. Inoltre alcuni casi limite devono essere gestiti in maniera specifica: ad esempio, usare 'rebase' su una branch con commit iniziale diverso. Git beneficerebbe della definizione del commit numero zero: non appena un deposito è costruito, HEAD verrebbe assegnato ad una stringa consistente in 20 bytes zero. Questo commit speciale rappresenterebbe un tree vuoto, senza genitori, che sarebbe presente in tutti i depositi Git. In questo modo ad esempio l'esecuzione di 'git log' informerebbe l'utente che non sono ancora stati fatti commit, invece di terminare con un 'fatal error'. Una cosa simile varrebbe per altri comandi. Ogni commit iniziale sarebbe implicitamente discendente da questo commit zero. Ci sarebbero tuttavia casi problematici. Se diverse branch con commit iniziali diversi fossero fusi assieme con un merge, l'uso del comando 'rebase' richiederebbe un sostanziale intervento manuale. === Bizzarrie dell'interfaccia === Dati due commit A e B, il significato delle espressioni "A..B" e "A...B" dipende da se il comando si attende due estremità o un intervallo. Vedete *git help diff* e *git help rev-parse*. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/it/branch.txt���������������������������������������������������������������������0000644�0001750�0001750�00000032103�12666307504�015411� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== La stregoneria delle branch == Le funzioni di merge e di ramificazione (o 'branch') sono le migliori "killer features" di Git. *Problema*: Fattori esterni conducono inevitabilmente a cambiamenti di contesto. Un grave bug si manifesta inaspettatamente nella versione di release. La scadenza per una particolare funzionalità viene anticipata. Uno sviluppatore che doveva collaborare con voi su una parte delicata di un progetto non è più disponibile. In ogni caso, dovete bruscamente smettere quello che stavate facendo per concentrarvi su un compito completamente diverso. Interrompere il flusso dei vostri pensieri può essere controproducente e, più scomodo è il cambiamento di contesto, più grande è lo svantaggio. Con un sistema di controllo di versione centralizzato bisognerebbe scaricare una nuova copia del lavoro dal server centrale. Un sistema decentralizzato è migliore perché permette di clonare localmente la versione che si vuole. Ma clonare richiede comunque di copiare un'intera cartella di lavoro, in aggiunta all'intera storia fino al punto voluto. Anche se Git riduce i costi tramite la condivisione di file e gli hard link, i file di progetto stessi devono essere ricreati interamente nella nuova cartella di lavoro. *Soluzione*: Git ha un metodo migliore per queste situazioni che è molto migliore ed efficiente in termini di spazio che il clonaggio: il comando *git branch*. Grazie a questa parola magica i file nella directory si trasformano immediatamente da una versione a un'altra. Questa trasformazione può fare molto di più che portarvi avanti e indietro nella storia del progetto. I vostri file possono trasformarsi dall'ultima release alla versione corrente di sviluppo, alla versione di un vostro collega, ecc. === Boss key === Avete mai giocato ad uno di quei giochi che possiedono un tasto (il ``boss key``) che nasconde immediatamente la schermata coprendola con qualcosa come una tabella di calcolo? In questo modo, se il vostro capo, entra nel vostro ufficio mentre state giocando potete nasconderlo rapidamente. In una cartella vuota eseguite: $ echo "Sono più intelligente che il mio capo." > myfile.txt $ git init $ git add . $ git commit -m "Commit iniziale" Avete appena creato un deposito Git che gestisce un file di testo che contiene un certo messaggio. Adesso digitate: $ git checkout -b capo # niente sembra essere cambiato dopo questo $ echo "Il mio capo è più intelligente di me." > myfile.txt $ git commit -a -m "Un altro commit" Tutto sembra come se aveste semplicemente sovrascritto il vostro file e messo in commit le modifiche. Ma questo non è che un'illusione. Ora digitate: $ git checkout master # Passa alla versione originale del file e voilà! Il file di testo è ritornato alla versione originale. E se il vostro capo si mettesse a curiosare in questa cartella eseguite: $ git checkout capo # Passa alla versione accettabile dal capo Potete passare da una versione all'altra in qualsiasi momento, e mettere in commit le vostre modifiche per ognuna indipendentemente. === Lavoro temporaneo === [[branch]] Diciamo che state lavorando ad una funzionalità e, per qualche ragione, dovete ritornare a tre versioni precedenti e temporaneamente aggiungere qualche istruzione per vedere come funziona qualcosa. Fate: $ git commit -a $ git checkout HEAD~3 Ora potete aggiungere codice temporaneo ovunque vogliate. Potete addirittura fare un commit dei cambiamenti. Quando avete finito eseguite: $ git checkout master per ritornare al vostro lavoro originario. Ricordatevi che i cambiamenti non sottomessi ad un commit andranno persi. Che fare se nonostante tutto voleste salvare questi cambiamenti temporanei? Facile: $ git checkout -b temporaneo e fate un commit prima di ritornare alla branch master. Qualora voleste ritornare ai cambiamenti temporanei, eseguite semplicemente: $ git checkout temporaneo Abbiamo già parlato del comando _checkout_ in un capitolo precedente, mentre discutevamo il caricamento di vecchi stati. Ne parleremo ancora più avanti. Per ora ci basta sapere questo: i file vengono cambiati allo stato richiesto, ma bisogna lasciare la branch master. A partire da questo momento, tutti i commit porteranno i vostri file su una strada diversa che potrà essere nominata più avanti. In altre parole, dopo un checkout verso uno stato precedente, Git ci posiziona automaticamente in una nuova branch anonima che potrà essere nominata e salvata con *git checkout -b*. === Correzioni rapide === Diciamo che state lavorando su qualcosa e vi viene improvvisamente richiesto di lasciar perdere tutto per correggere un bug appena scoperto nella versione `1b6d...` : $ git commit -a $ git checkout -b correzioni 1b6d Poi, quando avete corretto il bug, eseguite: $ git commit -a -m "Bug corretto" $ git checkout master per riprendere il lavoro originario. Potete anche fare un 'merge' delle nuove correzioni del bug: $ git merge correzioni === Merge === Con alcuni sistemi di controllo di versione creare delle branch è molto facile, ma fare un merge è difficile. Com Git, fare un merge è così facile che potreste anche non accorgervi che lo state facendo. Infatti abbiamo già incontrato il merge molto tempo fa. Il comando *pull* recupera, ('fetch') una serie di versioni e le incorpora ('merge') nella branch corrente. Se non ci sono cambiamenti locali, il merge è un semplicemente salto in avanti (un _fast forward_), un caso degenere simile a ottenere la versione più recente in un sistema di controllo di versione centralizzato. Ma se ci sono cambiamenti locali, Git farà automaticamente un merge, riportando tutti i conflitti. Normalmente una versione ha una sola 'versione genitore', vale a dire la versione precedente. Fare un merge di brach produce una versione con almeno due genitori. Questo solleva la seguente domanda: a quale versione corrisponde `HEAD~10`? Visto che una versione può avere parecchi genitori, quali dobbiamo seguire? Si dà il caso che questa notazione si riferisce sempre al primo genitore. Questo è desiderabile perché la versione corrente diventa il primo genitore in un merge; e spesso si è più interessati ai cambiamenti fatti nella branch corrente, piuttosto che ai cambiamenti integrati dalle altre branch. Potete fare riferimento ad un genitore specifico con un accento circonflesso. Ad esempio, per vedere il log del secondo genitore: $ git log HEAD^2 Potete omettere il numero per il primo genitore. Ad esempio, per vedere le differenze con il primo genitore: $ git diff HEAD^ Potete combinare questa notazione con le altre. Ad esempio: $ git checkout 1b6d^^2~10 -b ancient inizia la nuova branch ``ancient'' nello stato corrispondente a 10 versioni precedenti il secondo genitore del primo genitore del commit il cui nome inizia con 1b6d. === Flusso di lavoro ininterrotto === Spesso in un progetto ``hardware'' la seconda tappa deve aspettare il completamento della prima. Un'automobile in riparazione deve rimanere bloccata in garage fino all'arrivo di una particolare parte di ricambio. Un prototipo deve aspettare la fabbricazione di un processore prima che la costruzione possa continuare. I progetti software possono essere simili. La seconda parte di una nuova funzionalità può dover aspettare fino a che la prima parte venga completata e testata. Alcuni progetti richiedono che il vostro codice sia rivisto prima di essere accettato. Siete quindi obbligati ad aspettare l'approvazione della prima parte prima di iniziare la seconda. Grazie alla facilità con cui si creano delle branch e si effettua un merge, si possono piegare le regole e lavorare sulla parte II prima che la parte I sia ufficialmente pronta. Supponiamo che avete fatto il commit della parte I e l'avete sottomessa per approvazione. Diciamo che siete nella branch `master`. Create allora una nuova branch così: $ git checkout -b part2 In seguito, lavorate sulla parte II, fate il commit dei cambiamenti quando necessario. Errare è umano, e spesso vorrete tornare indietro e aggiustare qualcosa nella parte I. Se siete fortunati, o molto bravi, potete saltare questo passaggio. $ git checkout master # Ritorno alla parte 1 $ correzione_problemi $ git commit -a # Commit delle correzioni. $ git checkout part2 # Ritorno alla parte 2. $ git merge master # Merge delle correzioni. Finalmente la parte I è approvata. $ git checkout master # Ritorno alla parte I. $ distribuzione files # Distribuzione in tutto il mondo! $ git merge part2 # Merge della parte II $ git branch -d part2 # Eliminazione della branch "part2" In questo momento siete di nuovo nella branch `master`, con la parte II nella vostra cartella di lavoro. È facile estendere questo trucco a qualsiasi numero di parti. È anche facile creare delle branch retroattivamente: supponiamo che ad un certo punto vi accorgete che avreste dovuto creare una branch 7 commit fa. Digitate allora: $ git branch -m master part2 # Rinomina la branch "master" con il nome "part2". $ git branch master HEAD~7 # Crea una nuova branch "master" 7 commits nel passato. La branch `master` contiene ora solo la parte I, e la branch `part2` contiene il resto. Noi siamo in questa seconda branch; abbiamo creato `master` senza spostarvici perché vogliamo continuare a lavorare su `part2`. Questo è inusuale. Fino ad ora spostavamo in una branch non appena la creavamo, come in: $ git checkout HEAD~7 -b master # Crea una branch, e vi si sposta. === Riorganizzare un pasticcio === Magari vi piace lavorare su tutti gli aspetti di un progetto nella stessa branch. Volete che i vostri lavori in corso siano accessibili solo a voi stessi e volete che altri possano vedere le vostre versioni solo quando sono ben organizzate. Cominciamo creando due branch: $ git branch ordine # Crea una branch per commit organizzati. $ git checkout -b pasticcio # Crea e si sposta in una branch in cui lavorare In seguito lavorate su tutto quello che volete: correggere bugs, aggiungere funzionalità, aggiungere codice temporaneo, e così via, facendo commit quando necessario. Poi: $ git checkout ordine $ git cherry-pick pasticcio^^ applica le modifiche della versione progenitore della corrente versione ``pasticcio'' alla versione ``ordine''. Con i cherry-pick appropriati potete costruire una branch che contiene solo il codice permanente e che raggruppa tutti i commit collegati. === Gestione di branch === Per ottenere una lista di tutte le branch, digitate: $ git branch Per default iniziate nella branch chiamata ``master''. Alcuni raccomandano di lasciare la branch ``master'' intatta e di creare nuove branch per le proprie modifiche. Le opzioni *-d* e *-m* permettono di cancellare e spostare (rinominare) le branch. Per più informazioni vedete *git help branch*. La branch ``master'' è una convenzione utile. Gli altri possono assumere che il vostro deposito ha una branch con quel nome, e che questa contiene la versione ufficiale del vostro progetto. Nonostante sia possibile rinominare o cancellare la branch ``master'', può essere utile rispettare le tradizioni. === Branch temporanee === Dopo un certo tempo d'utilizzo potreste accorgervi che create frequentemente branch temporanee per ragioni simili: vi servono solamente per salvare lo stato corrente così da rapidamente saltare ad uno stato precedente per correggere un bug prioritario o qualcosa di simile. È analogo a cambiare temporaneamente canale televisivo per vedere cos'altro c'è alla TV. Ma invece di premere un paio di bottoni, dovete creare, spostarvi, fare merge e cancellare branch temporanee. Fortunatamente Git possiede una scorciatoia che è altrettanto pratica che il telecomando del vostro televisore: $ git stash Questo salva lo stato corrente in un posto temporaneo (uno 'stash') e ristabilisce lo stato precedente. La vostra cartella di lavoro appare esattamente com'era prima di fare le modifiche e potete correggere bugs, incorporare cambiamenti del deposito centrale (pull), e così via. Quando volete ritornare allo stato corrispondente al vostro 'stash', eseguite: $ git stash apply # Potreste dover risolvere qualche conflitto. Potete avere stash multipli, e manipolarli in modi diversi. Vedere *git help stash* per avere più informazioni. Come avrete indovinato, Git mantiene delle branch dietro le quinte per realizzare questi trucchi magici. === Lavorate come volete === Potreste chiedervi se vale la pena usare delle branch. Dopotutto creare dei cloni è un processo altrettanto rapido e potete passare da uno all'altro con un semplice *cd*, invece che gli esoterici comandi di Git. Consideriamo un browser web. Perché supportare tabs multiple oltre a finestre multiple? Perché permettere entrambi accomoda una gamma d'utilizzazione più ampia. Ad alcuni utenti piace avere una sola finestra e usare tabs per multiple pagine web. Altri insistono con l'estremo opposto: multiple finestre senza tabs. Altri ancora preferiscono qualcosa a metà. Le branch sono come delle tabs per la vostra cartella di lavoro, e i cloni sono come nuove finestre del vostro browser. Queste operazioni sono tutte veloci e locali. Quindi perché non sperimentare per trovare la combinazione che più vi si addice? Con Git potete lavorare esattamente come volete. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/it/secrets.txt��������������������������������������������������������������������0000644�0001750�0001750�00000032376�12666307504�015640� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Segreti rivelati == Diamo ora un'occhiata sotto il cofano e cerchiamo di capire come Git realizza i suoi miracoli. Per una descrizione approfondita fate riferimento al http://www.kernel.org/pub/software/scm/git/docs/user-manual.html[manuale utente]. === Invisibilità === Come fa Git ad essere così discreto? A parte qualche commit o merge occasionale, potreste lavorare come se il controllo di versione non esistesse. Vale a dire fino a che non è necessario, nel qual caso sarete felici che Git stava tenendo tutto sotto controllo per tutto il tempo. Altri sistemi di controllo di versione vi forzano costantemente a confrontarvi con scartoffie e burocrazia. File possono essere solo acceduti in lettura, a meno che non dite esplicitamente al server centrale quali file intendete modificare. I comandi di base soffrono progressivamente di problemi di performance all'aumentare del numero utenti. Il lavoro si arresta quando la rete o il server centrale hanno problemi. In contrasto, Git conserva tutta la storia del vostro progetto nella sottocartella `.git` della vostra cartella di lavoro. Questa è la vostra copia personale della storia e potete quindi rimanere offline fino a che non volete comunicare con altri. Avete controllo totale sul fato dei vostri file perché Git può ricrearli ad ogni momento a partire da uno stato salvato in `.git`. === Integrità === La maggior parte della gente associa la crittografia con la conservazione di informazioni segrete ma un altro dei suoi importanti scopi è di conservare l'integrità di queste informazioni. Un uso appropriato di funzioni hash crittografiche può prevenire la corruzione accidentale e dolosa di dati. Un codice hash SHA1 può essere visto come un codice unico di identificazione di 160 bit per ogni stringa di byte concepibile. Visto che un codice SHA1 è lui stesso una stringa di byte, possiamo calcolare un codice hash di stringe di byte che contengono altri codici hash. Questa semplice osservazione è sorprendentemente utile: cercate ad esempio 'hash chains'. Più tardi vedremo come Git usa questa tecnica per garantire efficientemente l'integrità di dati. Brevemente, Git conserva i vostri dati nella sottocartella `.git/objects`, ma invece di normali nomi di file vi troverete solo dei codici. Utilizzando questi codici come nomi dei file, e grazie a qualche trucco basato sull'uso di 'lockfile' e 'timestamping', Git trasforma un semplice sistema di file in un database efficiente e robusto. === Intelligenza === Come fa Git a sapere che avete rinominato un file anche se non gliel'avete mai detto esplicitamente? È vero, magari avete usato *git mv*, ma questo è esattamente la stessa cosa che usare *git rm* seguito da *git add*. Git possiede dei metodi euristici stanare cambiamenti di nomi e copie tra versioni successive. Infatti, può addirittura identificare lo spostamento di parti di codice da un file ad un altro! Pur non potendo coprire tutti i casi, questo funziona molto bene e sta sempre costantemente migliorando. Se non dovesse funzionare per voi, provate le opzioni che attivano metodi di rilevamento di copie più impegnative, e considerate l'eventualità di fare un aggiornamento === Indicizzazione === Per ogni file in gestione, Git memorizza delle informazioni, come la sua taglia su disco, e le date di creazione e ultima modifica, un file detto 'indice'. Per determinare su un file è stato cambiato, Git paragona il suo stato corrente con quello che è memorizzato nell'indice. Se le due fonti di informazione corrispondono Git non ha bisogno di rileggere il file. Visto che l'accesso all'indice è considerabilmente più che leggere file, se modificate solo qualche file, Git può aggiornare il suo stato quasi immediatamente. Prima abbiamo detto che l'indice si trova nell'area di staging. Com'è possibile che un semplice file contenente dati su altri file si trova nell'area di staging? Perché il comando 'add' aggiunge file nel database di Git e aggiorna queste informazioni, mentre il comando 'commit' senza opzioni crea un commit basato unicamente sull'indice e i file già inclusi nel database. === Le origini di Git === Questo http://lkml.org/lkml/2005/4/6/121[messaggio della mailing list del kernel di Linux] descrive la catena di eventi che hanno portato alla creazione di Git. L'intera discussione è un affascinante sito archeologico per gli storici di Git. === Il database di oggetti === Ognuna delle versioni dei vostri dati è conservata nel cosiddetto 'database di oggetti' che si trova nella sottocartella `.git/objects`; il resto del contenuto di `.git/` rappresenta meno dati: l'indice, il nome delle branch, le tags, le opzioni di configurazione, i logs, la posizione attuale del commit HEAD, e così via. Il database di oggetti è semplice ma elegante, e è la fonte della potenza di Git. Ogni file in `.git/objects` è un 'oggetto'. Ci sono tre tipi di oggetti che ci riguardano: oggetti 'blob', oggetti 'albero' (o `tree`) e gli oggetti 'commit'. === Oggetti 'blob' === Prima di tutto un po' di magia. Scegliete un nome di file qualsiasi. In una cartella vuota eseguite: $ echo sweet > VOSTRO_FILE $ git init $ git add . $ find .git/objects -type f Vedrete +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. Come posso saperlo senza sapere il nome del file? Perché il codice hash SHA1 di: "blob" SP "6" NUL "sweet" LF è aa823728ea7d592acc69b36875a482cdf3fd5c8d, dove SP è uno spazio, NUL è un carattere di zero byte e LF un passaggio a nuova linea. Potete verificare tutto ciò digitando: $ printf "blob 6\000sweet\n" | sha1sum Git utilizza un sistema di classificazione per contenuti: i file non sono archiviati secondo il loro nome, ma secondo il codice hash del loro contenuto, in un file che chiamiamo un oggetto 'blob'. Possiamo vedere il codice hash come identificativo unico del contenuto del file. Quindi, in un certo senso, ci stiamo riferendo ai file rispetto al loro contenuto. L'iniziale `blob 6` è semplicemente un'intestazione che indica il tipo di oggetto e la sua lunghezza in bytes; serve a semplificare la gestione interna. Ecco come ho potuto predire il contenuto di .git. Il nome del file non conta: solo il suo contenuto è usato per costruire l'oggetto blob. Magari vi state chiedendo che cosa succede nel caso di file identici. Provate ad aggiungere copie del vostro file, con qualsiasi nome. Il contenuto di +.git/objects+ rimane lo stesso a prescindere del numero di copie aggiunte. Git salva i dati solo una volta. A proposito, i file in +.git/objects+ sono copressi con zlib e conseguentemente non potete visualizzarne direttamente il contenuto. Passatele attraverso il filtro http://www.zlib.net/zpipe.c[zpipe -d], o eseguite: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d che visualizza appropriatamente l'oggetto scelto. === Oggetti 'tree' === Ma dove vanno a finire i nomi dei file? Devono essere salvati da qualche parte. Git si occupa dei nomi dei file in fase di commit: $ git commit # Scrivete un messaggio $ find .git/objects -type f Adesso dovreste avere tre oggetti. Ora non sono più in grado di predire il nome dei due nuovi file, perché dipenderà in parte dal nome che avete scelto. Procederemo assumendo che avete scelto ``rose''. Se questo non fosse il caso potete sempre riscrivere la storia per far sembrare che lo sia: $ git filter-branch --tree-filter 'mv NOME_DEL_VOSTRO_FILE rose' $ find .git/objects -type f Adesso dovreste vedere il file +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+ perché questo è il codice hash SHA1 del contenuto seguente: "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Verificate che questo file contenga il contenuto precedente digitando: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch È più facile verificare il codice hash con zpipe: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum Verificare l'hash è più complicato con il comando cat-file perché il suo output contiene elementi ulteriori oltre al file decompresso. Questo file è un oggetto 'tree': una lista di elementi consistenti in un tipo di file, un nome di file, e un hash. Nel nostro esempio il tipo di file è 100644, che indica che `rose` è un file normale e il codice hash e il codice hash è quello di un oggetto di tipo 'blob' che contiene il contenuto di `rose`. Altri possibili tipi di file sono eseguibili, link simbolici e cartelle. Nell'ultimo caso il codice hash si riferisce ad un oggetto 'tree'. Se avete eseguito filter-branch avrete dei vecchi oggetti di cui non avete più bisogno. Anche se saranno cancellati automaticamente dopo il periodo di ritenzione automatica, ora li cancelleremo per rendere il nostro esempio più facile da seguire $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune Nel caso di un vero progetto dovreste tipicamente evitare comandi del genere, visto che distruggono dei backup. Se volete un deposito più ordinato, è normalmente consigliabile creare un nuovo clone. Fate inoltre attenzione a manipolare direttamente il contenuto di +.git+: che cosa succederebbe se un comando Git è in esecuzione allo stesso tempo, o se se ci fosse un improvviso calo di corrente? In generale i refs dovrebbero essere cancellati con *git update-ref -d*, anche se spesso sembrerebbe sicuro cancella re +refs/original+ a mano. === Oggetti 'commit' === Abbiamo spiegato 2 dei 3 tipi di oggetto. Il terzo è l'oggetto 'commit'. Il suo contenuto dipende dal messaggio di commit, come anche dalla data e l'ora in cui è stato creato. Perché far in maniera di ottenere la stessa cosa dobbiamo fare qualche ritocco: $ git commit --amend -m Shakespeare # Cambiamento del messaggio di commit $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Ritocco della data di creazione e degli autori $ find .git/objects -type f Dovreste ora vedere il file +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ che è il codice hash SHA1 del suo contenuto: "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice <alice@example.com> 1234567890 -0800" LF "committer Bob <bob@example.com> 1234567890 -0800" LF LF "Shakespeare" LF Come prima potete utilizzare zpipe o cat-file per verificare voi stessi. Questo è il primo commi, non ci sono quindi commit genitori. Ma i commit seguenti conterranno sempre almeno una linea che identifica un commit genitore. === Indistinguibile dalla magia === I segreti di Git sembrano troppo semplici. Sembra che basterebbe mescolare assieme qualche script shell e aggiungere un pizzico di codice C per preparare un sistema del genere in qualche ora: una combinazione di operazioni di filesystem di base e hashing SHA1, guarnito con lockfile e file di sincronizzazione per avere un po' di robustezza. Infatti questaè un descrizione accurata le prime versioni di Git. Malgrado ciò, a parte qualche astuta tecnica di compressione per risparmiare spazio e di indicizzazione per risparmiare tempo, ora sappiamo come Git cambia abilmente un sistema di file in un perfetto database per il controllo di versione. Ad esempio, se un file nel database degli oggetti è corrotto da un errore sul disco i codici hash non corrisponderanno più e verremo informati del problema. Calcolando il codice hash del codice hash di altri oggetti è possibile garantire integrità a tutti i livelli. I commit sono atomici, nel senso che un commit non può memorizzare modifiche parziali: possiamo calcolare il codice hash di un commit e salvarlo in un database dopo aver creato i relativi oggetti 'tree', 'blob' e 'commit'. Il database degli oggetti è immune da interruzioni inaspettate dovute ad esempio a cali di corrente. Possiamo anche far fronte ai tentativi di attacco più maliziosi. Supponiamo ad esempio che un avversario tenti di modificare di nascosto il contenuto di un file in una vecchia versione di un progetto. Per rendere il database degli oggetti coerente, il nostro avversario deve anche modificare il codice hash dei corrispondenti oggetti blob, visto che ora sarà una stringa di byte diversa. Questo significa che dovrà cambiare il codice hash di tutti gli oggetti tree che fanno riferimento al file, e di conseguenza cambiare l'hash di tutti gli oggetti commit in ognuno di questi tree, oltre ai codici hash di tutti i discendenti di questi commit. Questo implica che il codice hash dell'HEAD ufficiale differirà da quello del deposito corrotto. Seguendo la traccia di codici hash erronei possiamo localizzare con precisione il file corrotto, come anche il primo commit ad averlo introdotto. In conclusione, purché i 20 byte che rappresentano l'ultimo commit sono al sicuro, è impossibile manomettere il deposito Git. Che dire delle famose funzionalità di Git? Della creazione di branch? Dei merge? Delle tag? Semplici dettagli. L'HEAD corrente è conservata nel file +.git/HEAD+ che contiene un codice hash di un oggetto commit. Il codice hash viene aggiornato durante un commit e l'esecuzione di molti altri comandi. Le branch funzionano in maniera molto simile: sono file in +.git/refs/heads+. La stessa cosa vale per le tag, salvate in +.git/refs/tags+ ma sono aggiornate da un insieme diverso di comandi. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/it/multiplayer.txt����������������������������������������������������������������0000644�0001750�0001750�00000023764�12666307504�016540� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Git multi-giocatore == Inizialmente usavo Git per progetti privati dove ero l'unico sviluppatore. Tra i comandi legati alla natura distribuita di Git, avevo bisogno solamente di *pull* e *clone* così da tenere lo stesso progetto in posti diversi. Più tardi ho voluto pubblicare il mio codice tramite Git e includere modifiche di diversi contributori. Ho dovuto imparare a gestire progetti con multipli sviluppatori da tutto il mondo. Fortunatamente questo è il punto forte di Git, e probabilmente addirittura la sua ragion d'essere. === Chi sono? === Ogni commit ha il nome e l'indirizzo e-mail di un autore, i quali sono mostrati dal comando *git log*. Per default Git utilizza i valori di sistemamastery per definire questi campi. Per configurarli esplicitamente, digitate: $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com Omettete l'opzione '--global' per configurare questi valori solo per il deposito corrente. === Git via SSH e HTTP === Supponiamo che avete un accesso SSH a un server web sul quale Git non è però installato. Anche se meno efficiente rispetto al suo protocollo nativo, Git può comunicare via HTTP. Scaricate, compilate e installate Git sul vostro conto, e create un deposito nella vostra cartella web: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update Con versioni meno recenti di Git il comando di copia non funziona e dovete eseguire: $ chmod a+x hooks/post-update Ora potete trasmettere le vostre modifiche via SSH da qualsiasi clone: $ git push web.server:/path/to/proj.git master e chiunque può ottenere il vostro progetto con: $ git clone http://web.server/proj.git === Git tramite qualsiasi canale === Volete sincronizzare dei depositi senza server o addirittura senza connessione di rete? Avete bisogno di improvvisare durante un'emergenza? abbiamo già visto che<<makinghistory, *git fast-export* e *git fast-import* possono convertire depositi in un semplice file, e viceversa>>. Possiamo quindi inviare questo tipo di file avanti e indietro per trasportare depositi Git attraverso un qualsiasi canale. Ma uno strumento più efficace è il comando *git bundle*. Il mittente crea un pacchetto, detto 'bundle': $ git bundle create qualche_file HEAD poi trasmette il bundle, +qualche_file+, al destinatario attraverso qualsiasi metodo: email, chiave USB, stampa e riconoscimento caratteri, lettura di bit via telefono, segnali di funo, ecc. Il destinatario può recuperare i commit dal bundle digitando: $ git pull qualche_file Il destinatario può effettuare ciò anche in deposito interamente vuoto. Malgrado la sua dimensione, +qualche_file+ contiene l'intero deposito Git originario. Nel caso di progetti grandi, riducete gli sprechi includendo nel bundle solo i cambiamenti che mancano nell'altro deposito. Per esempio, supponiamo che il commit ``1b6d...'' è il commit più recente che è condiviso dai due depositi. Possiamo ora eseguire: $ git bundle create qualche_file HEAD ^1b6d Se fatta di frequente, potremmo facilmente dimenticare quale commit è stato mandato per ultimo. La pagina d'aiuto suggerisce di utilizzare delle 'tag' per risolvere questo problema. In pratica, appena dopo aver inviato il bundle, digitate: $ git tag -f ultimo_bundle HEAD e create un nuovo bundle con: $ git bundle create nuovo_bundle HEAD ^ultimo_bundle === Le patch: la moneta di scambio globale === Le patch sono delle rappresentazioni testuali dei vostri cambiamenti che possono essere facilmente comprensibili sia per computer che umani. È quello che le rende interessanti. Potete mandare una patch per email ad altri sviluppatori indipendentemente dal sistema di controllo di versione che utilizzano. A partire dal momento che possono leggere le loro email, possono vedere le vostre modifiche. Similarmente, da parte vostra non avete bisogno che di un indirizzo email: non c'è neanche bisogno di avere un deposito Git online Ricordatevi dal primo capitolo, il comando: $ git diff 1b6d > my.patch produce una patch che può essere incollata in un'email per discussioni. In un deposito Git, eseguite: $ git apply < my.patch per applicare la patch. In un contesto più formale, quando è il nome e magari la firma dell'autore devono essere presenti, generate le patch a partire da un certo punto digitando: $ git format-patch 1b6d I file risultanti possono essere passati a *git-send-email*, o inviati a mano. Potete anche specificare un intervallo tra due commit: $ git format-patch 1b6d..HEAD^^ Dalla parte del destinatario salvate l'email in un file (diciamo 'email.txt') e poi digitate: $ git am < email.txt Questo applica le patch ricevute e crea inoltre un commit, includendo informazioni come il nome dell'autore. Se utilizzate un client email in un navigatore web potreste dover cercare il modo di vedere il messaggio nel suo formato "raw" originario prima di salvare la patch come file. Ci sono delle leggere differenze nel caso di client email che si basano sul formato mbox, ma se utilizzate uno di questi, siete probabilmente il tipo di persona che riesce a risolverle senza bisogno di leggere questo tutorial! === Ci dispiace, abbiamo cambiato indirizzo === Dopo aver conato un deposito, l'esecuzione di *git push* o *git pull* farà automaticamente riferimento all'URL del deposito d'origine. Come fa Git? Il segreto risiede nelle opzioni di configurazione create durante la clonazione. Diamoci un'occhiata: $ git config --list L'opzione +remote.origin.url+ determina l'URL della sorgente; ``origin'' è l'alias del deposito d'origina. Come per la convenzione di nominare ``master'' la branch principale, possiamo cambiare o cancellare questo alias ma non c'è normalmente nessuna ragione per farlo. Se l'indirizzo del deposito originario cambia, potete modificare il suo URL con: $ git config remote.origin.url git://new.url/proj.git L'opzione +branch.master.merge+ specifica la branch di default utilizzata dal comando *git pull*. Al momento della clonazione iniziale il nome scelto è quello della branch corrente del deposito originario. Anche se l'HEAD del deposito d'origine è spostato verso un'altra branch, il comando pull continuerà a seguire fedelmente la branch iniziale. Quest'opzione si applicherà unicamente al deposito usato nel clonazione iniziale, cioè quello salvato nell'opzione +branch.master.remote+. Se effettuiamo un pull da un altro deposito dobbiamo indicare esplicitamente quale branch vogliamo: $ git pull git://example.com/other.git master Questo spiega tra l'altro come mai alcuni dei precedenti esempi di 'push' e 'pull' non avevano nessun argomento. === Branch remote === Quando cloniamo un deposito, cloniamo anche tutte le sue branch. Magari non ve ne siete accorti perché Git le nascondei: dovete chiedere esplicitamente di vederle. Questo impedisce alle branch del deposito remoto d'interferire con le vostre branch, e rende l'uso di Git più facile per i novizi. Per ottenere una lista delle branch remote eseguite: $ git branch -r Dovreste ottenere qualcosa come: origin/HEAD origin/master origin/experimental Questi sono le branch e l'HEAD del deposito remoto, e possono essere usati in normali comandi Git. Supponiamo per esempio di aver fatto molti commit e che ora volete paragonare le differenze con l'ultima versione ottenibile con fetch. Potreste cercare nel log il codice SHA1 appropriato, ma è molto più semplice scrivere: $ git diff origin/HEAD Oppure potete anche vedere che cosa sta succedendo nella branch ``experimental':' $ git log origin/experimental === Depositi remoti multipli === Supponiamo che due altri sviluppatori stanno lavorando sul vostro progetto, e che vogliate tenerli d'occhio entrambi. Possiamo seguire più depositi allo stesso tempo con: $ git remote add altro git://example.com/un_deposito.git $ git pull altro una_branch Ora abbiamo fatto un merge con una branch di un secondo deposito e possiamo avere facile accesso a tutte le branch di tutti i depositi: $ git diff origin/experimental^ altro/una_branch~5 Ma come fare se vogliamo solo paragonare i loro cambiamenti senza modificare il nostro lavoro? I altre parole, vogliamo esaminare le loro branch senza che le loro modifiche invadano la nostra cartella di lavoro. In questo caso, invece di fare un pull, eseguite: $ git fetch # Fetch dal deposito d'origine, il default $ git fetch altro # Fetch dal secondo programmatore. Questo fa un fetch solamente delle storie. Nonostante la cartella di lavoro rimane intatta, possiamo riferirci a qualsiasi branch in qualsiasi deposito con i comandi Git, perché ora abbiamo una copia locale. Ricordatevi che dietro le quinte, un *pull* è semplicemente un *fetch* seguito da un *merge*. Normalmente facciamo un *pull* perché vogliamo ottenere un merge delle ultime modifiche dopo aver fatto un fetch. La situazione precedente è una notevole eccezione. Guardate *git help remote* per sapere come eliminare depositi remoti, ignorare delle branch, e ancora di più. === Le mie preferenze === Per i miei progetti mi piace che i contributori preparino depositi dai quali posso fare in pull. Alcuni servizi di host Git permettono di creare i vostri cloni di un progetto con il click di un bottone. Dopo aver fatto il fetch di una serie di modifiche, utilizzo i comandi Git per navigare e esaminare queste modifiche che, idealmente, saranno ben organizzate e descritte. Faccio il merge dei miei cambiamenti, e forse qualche modifica in più. Una volta soddisfatto, faccio un push verso il deposito principale. Nonostante non riceva molto spesso dei contributi, credo che questo approccio scali bene. In proposito, vi consiglio di guardare http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[ questo post di Linus Torvalds]. Restare nel mondo di Git è un po' più pratiche che usare file di patch, visto che mi risparmia di doverli convertire in commit Git. Inoltre, Git gestisce direttamente dettagli come salvare il nome e l'indirizzo email dell'autore, così come la data e l'ora, e chiede anche all'autore di descrivere i cambiamenti fatti. ������������gitmagic-20160304/it/preface.txt��������������������������������������������������������������������0000644�0001750�0001750�00000010334�12666307504�015563� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������= Git Magic = Ben Lynn Agosto 2007 == Prefazione == http://git.or.cz/[Git] è il coltellino svizzero per il controllo di versioni. Uno strumento per la gestione di revisioni affidabile, versatile e multifunzionale, ma la cui flessibilità ne rende difficile l'apprendimento, e ancora di più la padronanza. Come osserva Arthur C. Clarke, qualunque tecnologia sufficientemente avanzata è indistinguibile dalla magia. Questo è un buon atteggiamento con cui approcciare Git: i novizi possono ignorare i suoi meccanismi interni e utilizzare Git come fosse una bacchetta magica con cui meravigliare gli amici e far infuriare gli avversari. Invece di dilungarci nei dettagli, vedremo quali istruzioni vanno usate per ottenere risultati specifici. In seguito, grazie all'uso ripetuto, capiremo il funzionamento di ognuno dei trucchi, e come possono essere combinati per sopperire alle nostre necessità. .Traduzioni - link:/\~blynn/gitmagic/intl/zh_cn/[Cinese semplificato]: JunJie, Meng e JiangWei. Conversione in link:/~blynn/gitmagic/intl/zh_tw/[Cinese tradizionale] tramite +cconv -f UTF7-CN -t UTF8-TW+. - link:/~blynn/gitmagic/intl/fr/[Francese]: Alexandre Garel, Paul Gaborit, e Nicolas Deram. Anche scaricabile da http://tutoriels.itaapy.com/[itaapy]. - link:/~blynn/gitmagic/intl/de/[Tedesco]: Benjamin Bellee e Armin Stebich; anche scaricabile dal http://gitmagic.lordofbikes.de/[sito web di Armin]. - link:/~blynn/gitmagic/intl/it/[Italiano]: Mattia Rigotti - http://www.slideshare.net/slide_user/magia-git[Portoghese]: Leonardo Siqueira Rodrigues [http://www.slideshare.net/slide_user/magia-git-verso-odt[formato ODT]]. - link:/~blynn/gitmagic/intl/ru/[Russo]: Tikhon Tarnavsky, Mikhail Dymskov, e altri. - link:/~blynn/gitmagic/intl/es/[Spagnolo]: Rodrigo Toledo e Ariset Llerena Tapia. - link:/~blynn/gitmagic/intl/uk/[Ucraino]: Volodymyr Bodenchuk. - link:/~blynn/gitmagic/intl/vi/[Vietnamita]: Trần Ngọc Quân; anche scaricabile dal http://vnwildman.users.sourceforge.net/gitmagic/[suo sito web]. scaricabile dal suo sito .Altre edizioni - link:book.html[Pagina web individuale] : simplice documento HTML, senza CSS ; - link:book.pdf[File PDF]: versione stampabile. - http://packages.debian.org/gitmagic[Pacchetto Debian], http:://packages.ubuntu.com/gitmagic[pacchetto Ubuntu]: ottenete rapidamente una copia locale. Pratico quando http://csdcf.stanford.edu/status/[questo server] non è raggiungibile. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Libro vero e proprio [Amazon.com]]: 64 pagine, 15.24cm x 22.86cm, bianco e nero, in inglese. Pratico in caso di mancanza di elettricità. === Grazie! === Voglio ringraziare le persone che hanno prestato il loro lavoro per tradurre queste pagine. Sono grato di poter raggiungere un numero più ampio di lettori grazie agli sforzi delle persone appena citate. Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin e Tyler Breisacher hanno contribuito con le loro correzioni e miglioramenti. François Marier mantiene il pacchetto Debian, creato originariamente da Daniel Baumarr. Sono anche grato a molti altri per i loro sostegno e incoraggiamenti. Sono tentato di menzionarvi qui, ma rischierei di alzare eccessivamente le vostre aspettative. Se per errore ho dimenticato di menzionare qualcuno, fatemelo per favore sapere, o mandatemi semplicemente una patch! === Licenza === Questo manuale è pubblicato sotto la licenza http://www.gnu.org/licenses/gpl-3.0.html[GNU General Public License version 3]. Naturalmente il codice sorgente è disponibile come deposito Git, e si può ottenere digitando: $ git clone git://repo.or.cz/gitmagic.git # Crea la cartella "gitmagic" oppure da altri server: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git $ git clone https://code.google.com/p/gitmagic/ $ git clone git://git.assembla.com/gitmagic.git $ git clone git@bitbucket.org:blynn/gitmagic.git GitHub, Assembla, e Bitbucket supportano deposito privati, gli ultimi due sono gratuiti. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/it/basic.txt����������������������������������������������������������������������0000644�0001750�0001750�00000020441�12666307504�015237� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Trucchi di base == Piuttosto che immergerci nel mare di comandi di Git, bagniamoci un po' i piedi con i seguenti esempi elementari. Nonostante la loro semplicità, ognuno di loro è utile. In effetti, durante i miei mesi iniziali d'utilizzazione di Git, non mi sono mai avventurato al di là di del materiale in questo capitolo. === Salvare lo stato corrente === Siete sul punto di fare qualcosa di drastico? Prima di proseguire, catturate lo stato di tutti i file nella directory corrente: $ git init $ git add . $ git commit -m "Il mio primo backup" Qualora le cose dovessero andare per il peggio, potrete sempre ripristinare la versione salvate: $ git reset --hard Per salvare un nuovo state: $ git commit -a -m "Un altro backup" === Aggiungere, rimuovere e rinominare file === Le istruzioni che abbiamo appena visto tengono traccia solo dei file che erano presenti nel momento dell'esecuzione di *git add*. Ma se aggiungete nuovi file o sottocartelle, dovrete dirlo a Git: $ git add readme.txt Documentation Analogamente se volete che Git ignori alcuni file: $ git rm kludge.h obsolete.c $ git rm -r incriminating/evidence/ Git rimuoverà questi file per voi, se non l'avete ancora fatto. Un file può essere rinominato rimuovendo il vecchio nome e aggiungendo il nuovo nome. È anche possibile usare la scorciatoia *git mv* che segue la stessa sintassi del comando *mv*. Ad esempio: $ git mv bug.c feature.c === Annullare/Ripristino avanzati === A volte può capitare che vogliate solamente ritornare indietro e dimenticare le modifiche effettuate dopo un certo punto, perché sono tutte sbagliate. In quel caso: $ git log vi nostra una lista dei commit più recenti, accompagnati dal loro codice SHA1: ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob <bob@example.com> Date: Tue Mar 14 01:59:26 2000 -0800 Sostituzione di prinf() con write() commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alice <alice@example.com> Date: Thu Jan 1 00:00:00 1970 +0000 Commit iniziale ---------------------------------- I primi caratteri del codice SHA1 sono sufficienti per specificare un commit; alternativamente copiate e incollate l'intero codice SHA1. Digitate: $ git reset --hard 766f per reinstaurare lo stato corrispondente al commit corrente e permanentemente cancellare i commit più recenti. Altre volte potrebbe capitarvi di voler fare solo un breve salto in uno stato precedente. In questo caso eseguite: $ git checkout 82f5 Questo vi riporta indietro nel tempo, preservando i commit più recenti. Bisogna però sapere che, come in ogni viaggio nel tempo in un film di fantascienza, se ora modificate e sottomettete un commit vi ritroverete in una realtà alternativa, perché avrete fatto delle azioni differenti rispetto alla realtà originaria. Questa realtà parallela viene chiamata 'ramificazione' o 'branch', et <<branch,vi dirò di più in proposito in seguito>>. Per ora è abbastanza ricordare che $ git checkout master vi riporta al presente. Inoltre, per evitare che Git si lamenti, ricordatevi di fare un commit o un reset delle vostre modifiche prima di fare un checkout. Per riprendere l'analogia con i videogiochi digitate: - *`git reset --hard`* : carica un vecchio salvataggio e cancella tutte le partite salvate più recenti di quella appena caricata. - *`git checkout`* : carica una vecchia partita, ma se ci giocate, lo stato della partita sarà diverso da quello dei salvataggi successivi che avete fato inizialmente. Da ora in poi ogni volta che salvate una partita finirete in un branch separata che rappresenta la realtà parallela in cui siete entrati. <<branch,Ci occuperemo di questo più tardi>>. Potete scegliere di ripristinare file e sottocartelle particolari aggiungendoli alla fine del seguente comando: $ git checkout 82f5 un.file un-altro.file Fate però attenzione: questa forma di *checkout* può sovrascrivere dei file senza avvertimenti. Per evitare incidenti, fate un commit prima di eseguire un comando di checkout, specialmente se siete alle prime armi con Git. In generale, ogni volta che non siete sicuri delle conseguenze di comando, che sia di Git o no, eseguite prima *git commit -a*. Non vi piace copiare e incollare codice hash? Allora utilizzate: $ git checkout :/"Il mio primo b" per saltare direttamente al commit che inizia con quel messaggio. Potete anche chiedere, ad esempio, il quintultimo stato salvato: $ git checkout master~5 === Annullare (revert) === In una corte di giustizia, certi avvenimenti possono essere stralciati dal processo verbale. Analogamente, potete selezionare degli specifici commit da annullare. $ git commit -a $ git revert 1b6d annulla solo l'ultimo commit con il dato codice hash. Il revert viene registrato come un nuovo commit, fatto che potrete verificare eseguendo un *git log*. === Generare un diario delle modifiche (changelog) === Certi progetti richiedono un http://it.wikipedia.org/wiki/Changelog[changelog]. Createlo digitando: $ git log > ChangeLog === Scaricare dei files === Fate una copia di un progetto gestito da Git digitando: $ git clone git://server/percorso/verso/files Ad esempio, per ottenere tutti i file che ho usato per creare questo sito: $ git clone git://git.or.cz/gitmagic.git Avremo molto da dire a proposito del comando *clone* tra poco. === L'ultima versione === Se avete già scaricato una copia di un progetto usando *git clone*, potete aggiornarla all'ultima versione con: $ git pull === Pubblicazione istantanea === Immaginate di aver scritto uno script che volete condividere con altri. Potreste semplicemente dire loro di scaricarlo dal vostro computer, ma le fanno mentre state migliorando lo script o sperimentando con delle modifiche, potrebbero finire nei guai. Naturalmente, questo tipo di situazioni sono la ragione per cui esistono i cicli di rilascio. Gli sviluppatori possono lavorare frequentemente ad un progetto, ma rilasciano il codice solo quando hanno l'impressione che sia presentabile. Per fare questo con Git, nella cartella che contiene lo script eseguite: $ git init $ git add . $ git commit -m "Prima versione" In seguito dite agli utenti di eseguire: $ git clone il.vostro.computer:/percorso/verso/lo/script per scaricare il vostro script. Questo assume che tutti abbiamo accesso ssh al vostro computer. Se non fosse il caso, eseguite *git daemon* e dite ai vostri utenti di eseguire invece: $ git clone git://il.vostro.computer/percorso/verso/lo/script A partire da questo momento, ogni volta che il vostro script è pronto per essere rilasciato, eseguite: $ git commit -a -m "Nuova versione" e i vostri utenti potranno aggiornare la loro versione andando nella cartella che contiene lo script e digitando: $ git pull I vostri utenti non si ritroveranno mai più con una versione del vostro script che non volete che vedano. === Che cosa ho fatto? === Ritroverete le modifiche fatte dall'ultimo commit con: $ git diff Oppure quelle a partire da ieri con: $ git diff "@{yesterday}" O tra una versione specifica e due versioni fa: $ git diff 1b6d "master~2" In ogni caso il risultato è una patch che può essere applicata con *git apply*. Potete anche provare: $ git whatchanged --since="2 weeks ago" Spesso esamino invece la storia dei commits con http://sourceforge.net/projects/qgit[qgit], per via della sua sfolgorante interfaccia, oppure http://jonas.nitro.dk/tig/[tig], un'interfaccia in modalità testo che funziona bene anche con le connessioni più lente. Alternativamente, installate un server web, lanciate *git instaweb* e lanciate il vostro browser. === Esercizio === Siano A, B, C, D quattro commit successivi, dove B è identico a A, con l'eccezione che alcuni file sono stati rimossi. Vogliamo rimettere i file in D. Come possiamo fare? Ci sono almeno tre soluzioni. Assumiamo che siamo in D: 1. La differenza tra A e B sono i file rimossi. Possiamo creare una patch che rappresenti la differenza e applicarla: $ git diff B A | git apply 2. Visto che i files in questioni sono presenti in A, possiamo recuperarli: $ git checkout A foo.c bar.h 3. Possiamo anche pensare al passo da A a B come ad una modifica che vogliamo annullare: $ git revert B Quel è la scelta migliore? Quella che preferite! È facile ottenere quello che volete con Git, e spesso ci sono diversi modi di ottenerlo. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/it/grandmaster.txt����������������������������������������������������������������0000644�0001750�0001750�00000027060�12666307504�016471� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Padroneggiare Git == A questo punto dovreste essere capaci di navigare la guida *git help* e di capire quasi tutto (a condizione ovviamente di capire l'inglese). Nonostante ciò ritrovare il comando esatto richiesto per risolvere un particolare problema può essere tedioso. Magari posso aiutarvi a risparmiare un po' di tempo: qua sotto trovate qualcuna delle ricette di cui ho avuto bisogno in passato. === Pubblicazione di codice sorgente === Per i miei progetti Git gestisce esattamente i file che voglio archiviare e pubblicare. Per creare un archivio in formato tar del codice sorgente utilizzo: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === Commit dei cambiamenti === Dire a Git quando avete aggiunto, cancellato o rinominato dei file può essere fastidioso per certi progetti. Invece potete eseguire: $ git add . $ git add -u Git cercherà i file della cartella corrente e gestirà tutti i dettagli automaticamente. Invece del secondo comando 'add', eseguite `git commit -a` se volete anche fare un commit. Guardate *git help ignore* per sapere come specificare i file che devono essere ignorati. Potete anche effettuare tutti i passi precedenti in un colpo solo con: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove Le opzioni *-z* e *-0* permettono di evitare effetti collaterali dovuti a file il cui nome contiene strani caratteri. Visto che questo comando aggiunge anche file che sono ignorati, potreste voler usare le opzioni `-x` o `-X`. === Il mio commit è troppo grande! === Vi siete trascurati da un po' di tempo di fare dei commit? Avete scritto codice furiosamente dimenticandovi di controllo di versione? Avete implementato una serie di cambiamenti indipendenti, perché è il vostro stile di lavoro? Non c'è problema. Eseguite: $ git add -p Per ognuna delle modifiche che avete fatto, Git vi mostrerà la parte di codice che è stata cambiata e vi domanderà se dovrà fare parte del prossimo commit. Rispondete con "y" (sì) o con "n" (no). Avete anche altre opzioni, come di postporre la decisione; digitate "?" per saperne di più. Una volta soddisfatti, eseguite: $ git commit per fare un commit che comprende esattamente le modifiche selezionate (le modifiche `nell'area di staging`, vedere dopo). Assicuratevi di omettere l'opzione *-a*, altrimenti Git farà un commit che includerà tutte le vostre modifiche. Che fare se avete modificato molti file in posti diversi? Verificare ogni cambiamento uno alla volta diviene allora rapidamente frustrante e noioso. In questo caso usate *git add -i*, la cui interfaccia è meno intuitiva ma più flessibile. Con qualche tasto potete aggiungere o togliere più file alla volta dall'area di staging, oppure anche rivedere e selezionare cambiamenti in file particolari. Altrimenti potete anche eseguire *git commit \--interactive* che effettuerà automaticamente un commit quando avrete finito. === L'indice : l'area di staging === Fino ad ora abbiamo evitato il famoso 'indice' di Git, ma adesso dobbiamo parlarne per capire meglio il paragrafo precedente. L'indice è un'area temporanea di cosiddetto 'staging'. Git trasferisce raramente dati direttamente dal vostro progetto alla sua storia. Invece, Git scrive prima i dati nell'indice, e poi copia tutti i dati dell'indice nella loro destinazione finale. Un *commit -a* è ad esempio in realtà un processo a due fasi. La prima fase stabilisce un'istantanea (un cosiddetto 'snapshot') dello stato corrente di ogni file in gestione e la ripone nell'indice. La seconda fase salva permanentemente questo snapshot. Effettuare un commit senza l'opzione *-a* esegue solamente la seconda fase, e ha quindi solo senso solo a seguito di un comando che modifica l'indice, come ad esempio *git add*. Normalmente possiamo ignorare l'indice e comportandoci effettivamente come se se stessimo scambiando dati direttamente nella storia. In altri casi come quello precedente vogliamo un controllo più fine e manipoliamo quindi l'indice. Inseriamo nell'indice uno snapshot di alcuni, ma non tutti i cambiamenti, e poi salviamo permanentemente questi snapshot accuratamente costruiti. === Non perdete la "testa" === La tag HEAD è come un cursore che normalmente punta all'ultimo commit, avanzando con ogni commit. Alcuni comandi di Git permettono di muoverla. Ad esempio: $ git reset HEAD~3 sposta HEAD tre commit indietro. Da qua via tutti i comandi Git agiscono come se non aveste fatto quegli ultimi tre commit, mentre i vostri file rimangono nello stato presente. Vedere la pagina di help per qualche applicazione interessante. Ma come fare per ritornare al futuro? I commit passati non sanno niente del futuro. Se conoscete il codice SHA1 dell'HEAD originario (diciamo 1b6d...), fate allora: $ git reset 1b6d Ma come fare se non l'avete memorizzato? Non c'è problema: per comandi di questo genere Git salva l'HEAD originario in una tag chiamata ORIG_HEAD, e potete quindi ritornare al futuro sani e salvi con: $ git reset ORIG_HEAD === Cacciatore di "teste" === ORIG_HEAD può non essere abbastanza. Diciamo che vi siete appena accorti di un monumentale errore e dovete ritornare ad un vecchio commit in una branch dimenticata da lungo tempo. Per default Git conserva un commit per almeno due settimane, anche se gli avete ordinato di distruggere la branch lo conteneva. La parte difficile è trovare il codice hash appropriato. Potete sempre far scorrere tutti i codici hash il `.git/objects` e trovare quello che cercate per tentativi. C'è però un modo molto più facile. Git registra ogni codice hash che incontra in `.git/logs`. La sottocartella `refs` contiene la storia dell'attività di tutte le branch, mentre il file `HEAD` mostra tutti i codici hash che HEAD ha assunto. Quest'ultimo può usato per trovare commit di una branch che è stata accidentalmente cancellata. Il comando *reflog* provvede un'interfaccia intuitiva per gestire questi file di log. Provate a eseguire: $ git reflog Invece di copiare e incollare codici hash dal reflog, provate: $ git checkout "@{10 minutes ago}" O date un'occhiata al quintultimo commit visitato con: $ git checkout "@{5}" Vedete la sezione ``Specifying Revisions'' di *git help rev-parse* per avere più dettagli. Potreste voler configurare un periodo più lungo per la ritenzione dei commit da cancellare. Ad esempio: $ git config gc.pruneexpire "30 days" significa che un commit cancellato sarà perso permanentemente eliminato solo 30 giorni più tardi, quando *git gc* sarà eseguito. Potete anche voler disabilitare l'esecuzione automatica di *git gc*: $ git config gc.auto 0 nel qual caso commit verranno solo effettivamente eliminati all'esecuzione manuale di *git gc*. === Costruire sopra Git === In vero stile UNIX, il design di Git ne permette l'utilizzo come componente a basso livello di altri programmi, come interfacce grafiche e web, interfacce di linea alternative, strumenti di gestione di patch, programmi di importazione e conversione, ecc. Infatti, alcuni comandi Git sono loro stessi script che fanno affidamento ad altri comandi di base. Con un po' di ritocchi potete voi stessi personalizzare Git in base alle vostre preferenze. Un facile trucco consiste nel creare degli alias di comandi Git per abbreviare le funzioni che utilizzate di frequente: $ git config --global alias.co checkout $ git config --global --get-regexp alias # mostra gli alias correnti alias.co checkout $ git co foo # equivalente a 'git checkout foo' Un altro trucco consiste nell'integrare il nome della branch corrente nella vostra linea di comando o nel titolo della finestra. L'invocazione di $ git symbolic-ref HEAD mostra il nome completo della branch corrente. In pratica, vorrete probabilmente togliere "refs/heads/" e ignorare gli errori: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- La sottocartella +contrib+ è uno scrigno di utili strumenti basati su Git. Un giorno alcuni di questi potrebbero essere promossi al rango di comandi ufficiali. Su Debian e Ubuntu questa cartella si trova in +/usr/share/doc/git-core/contrib+. Uno dei più popolari tra questi script si trova in +workdir/git-new-workdir+. Grazie ad un link simbolico intelligente, questo script crea una nuova cartella di lavoro la cui storia è condivisa con il deposito originario: $ git-new-workdir un/deposito/esistente nuova/cartella La nuova cartella e i suoi file possono essere visti come dei cloni, salvo per il fatto che la storia è condivisa e quindi i rimane automaticamente sincronizzata. Non c'è quindi nessun bisogno di fare merge, push o pull. === Acrobazie audaci === Git fa in modo che sia difficile per un utilizzatore distruggere accidentalmente dei dati. Ma se sapete cosa state facendo, potete escludere le misure di sicurezza dei comandi più comuni. *Checkout*: 'Checkout' non funziona in caso di Modifiche non integrate con commit. Per distruggere i vostri cambiamenti ed effettuare comunque un certo checkout, usate la flag 'force': $ git checkout -f HEAD^ D'altro canto, se specificate un percorso particolare per il checkout, non ci sono controlli di sicurezza. I percorsi forniti sono silenziosamente sovrascritti. Siate cauti se utilizzate checkout in questa modalità. *Reset*: Anche 'reset' non funziona in presenza di cambiamenti non integrate con commit. Per forzare il comando, eseguite: $ git reset --hard 1b6d *Branch*: Non si possono cancellare branch se questo risulta nella perdita di cambiamenti. Per forzare l'eliminazione scrivete: $ git branch -D branch_da_cancellare # invece di -d Similmente, un tentativo di rinominare una branch con il nome di un'altra è bloccato se questo risulterebbe nella perdita di dati. Per forzare il cambiamento di nome scrivete: $ git branch -M origine destinazione # à invece di -m Contrariamente ai casi di 'checkout' e 'reset', questi ultimi comandi non effettuano un'eliminazione immediata dell'informazione. I cambiamenti sono salvati nella sottocartella .git, e possono essere recuperati tramite il corrispondente codice hash in `.git/logs` (vedete "Cacciatore di ``teste''" precedentemente). Per default, sono conservati per almeno due settimane. *Clean*: Alcuni comandi Git si rifiutano di procedere per non rischiare di danneggiare file che non sono in gestione. Se siete certi che tutti questi file possono essere sacrificati, allora cancellateli senza pietà con: $ git clean -f -d In seguito il comando precedentemente eccessivamente prudente funzionerà. === Prevenire commit erronei === Errori stupidi ingombrano i miei depositi. I peggiori sono quelli dovuti a file mancanti per via di un *git add* dimenticato. Altri errori meno gravi riguardano spazi bianchi dimenticati e conflitti di merge irrisolti: nonostante siano inoffensivi, vorrei che non apparissero nel registro pubblico. Se solo mi fossi premunito utilizzando dei controlli preliminari automatizzati, i cosiddetti _hook_, che mi avvisino di questi problemi comuni! $ cd .git/hooks $ cp pre-commit.sample pre-commit # Vecchie versioni di Git : chmod +x pre-commit Ora Git blocca un commit se si accorge di spazi inutili o se ci sono conflitti di merge non risolti. Per questa guida ho anche aggiunto le seguenti linee all'inizio del mio hook *pre-commit* per prevenire le mie distrazioni: if git ls-files -o | grep '\.txt$'; then echo FAIL! Untracked .txt files. exit 1 fi Molte operazioni di Git accettano hook; vedete *git help hooks*. Abbiamo già utilizzato l'hook *post-update* in precedenza, quando abbiamo discusso Git via HTTP. Questo è eseguito ogni volta che l'HEAD cambia. Lo script post-update d'esempio aggiorna i file Git necessari per comunicare dati via canali come HTTP che sono agnostici di Git. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/it/history.txt��������������������������������������������������������������������0000644�0001750�0001750�00000030552�12666307504�015663� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Lezioni di storia == Una delle conseguenze della natura distribuita di Git è che il corso storico può essere modificato facilmente. Ma se alterate il passato fate attenzione: riscrivete solo le parti di storia che riguardano solo voi. Nello stesso modo in cui nazioni dibattono le responsabilità di atrocità, se qualcun altro ha un clone la cui storia differisce dalla vostra, avrete problemi a riconciliare le vostre differenze. Certi sviluppatori insistono che la storia debba essere considerata immutabile, inclusi i difetti. Altri pensano invece che le strutture storiche debbano essere rese presentabili prima di essere presentate pubblicamente. Git è compatibile con entrambi i punti di vista. Come con l'uso di clone, branch e merge, riscrivere la storia è semplicemente un altra capacità che vi permette Git. Sta a voi farne buon uso. === Mi correggo === Avete appena fatto un commit, ma ora vi accorgete che avreste voluto scrivere un messaggio diverso? Allora eseguite: $ git commit --amend per modificare l'ultimo messaggio. Vi siete accorti di aver dimenticato di aggiungere un file? Allora eseguite *git add* per aggiungerlo, e eseguite il comando precedente. Volete aggiungere qualche modifica supplementare nell'ultimo commit? Allora fatele e eseguite: $ git commit --amend -a === ... e ancora di più === Supponiamo che il problema precedente è dieci volte peggio. Dopo una lunga seduta avete fatto parecchi commit. Ma non siete soddisfatto da come sono organizzati, e alcuni messaggi di commit potrebbero essere riscritti meglio. Allora digitate: $ git rebase -i HEAD~10 e gli ultimi 10 commit appariranno nel vostro $EDITOR di teso preferito. Ecco un piccolo estratto come esempio: pick 5c6eb73 Added repo.or.cz link pick a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile I commit più vecchi precedono quelli più recenti in questa lista, a differenza del comando `log`. Qua 5c6eb73 è il commit più vecchio e 100834f è il più recente. In seguito: - Rimuovete un commit cancellando la sua linea. È simile al comando revert, ma è come se il commit non fosse mai esistito. - Cambiate l'ordine dei commit cambiando l'ordine delle linee. - Sostituite `pick` con: * `edit` per marcare il commit per essere modificato. * `reword` per modificare il messaggio nel log. * `squash` per fare un merge del commit con quello precedente. * `fixup` per fare un merge di questo commit con quello precedente e rimuovere il messaggio nel log. Ad esempio, possiamo sostituire il secondo `pick` con `squash`: pick 5c6eb73 Added repo.or.cz link squash a311a64 Reordered analogies in "Work How 'ou Want" pick 100834f Added push target to Makefile Dopo aver salvato ed essere usciti dal file, Git fa un merge di a311a64 in 5c6eb73. Quindi *squash* fa un merge combinando le versioni nella versione precedente. Git quindi combina i loro messaggi e li presenta per eventuali modifiche. Il comando *fixup* salta questo passo; il messaggio log a cui viene applicato il comando viene semplicemente scartato. Se avete marcato un commit con *edit*, Git vi riporta nel passato, al commit più vecchio. Potete correggere il vecchio commit come descritto nella sezione precedente, e anche creare nuovi commit nella posizione corrente. Non appena siete soddisfatto con le rettifiche, ritornate in avanti nel tempo eseguendo: $ git rebase --continue Git ripercorre i commit fino al prossimo *edit*, o fino al presente se non ne rimane nessuno. Potete anche abbandonare il vostro tentativo di cambiare la storia con 'rebase' nel modo seguente: $ git rebase --abort Quindi fate dei commit subito e spesso: potrete mettere tutto in ordine più tardi con 'rebse'. === E cambiamenti locali per finire === State lavorando ad un progetto attivo. Fate alcuni commit locali, e poi vi sincronizzate con il deposito ufficiale con un merge. Questo ciclo si ripete qualche volta fino a che siete pronti a integrare a vostra volta i vostri cambiamenti nel deposito centrale con 'push'. Ma a questo punto la storia del vostro clone Git locale è un confuso garbuglio di modifiche vostre e ufficiali. Preferireste vedere tutti i vostri cambiamenti in una sezione contigua, seguita dai cambiamenti ufficiali. Questo è un lavoro per *git rebase* come descritto precedentemente. In molti casi potete usare la flag *--onto* per evitare interazioni. Leggete *git help rebase* per degli esempi dettagliati di questo fantastico comando. Potete scindere dei commit. Potete anche riarrangiare delle branch di un deposito. State attenti: rebase è un comando potente. In casi complessi fate prima un backup con *git clone*. === Riscrivere la storia === Occasionalmente c'è bisogno di fare delle modifiche equivalenti a cancellare con persone da una foto ufficiale, cancellandole dalla storia in stile Stalinista. Per esempio, supponiamo che avete intenzione di pubblicare un progetto, ma che questo include un file che per qualche ragione volete tenere privato. Diciamo ad esempio che ho scritto il mio numero di carta di credito in un file che ho aggiunto per sbaglio al progetto. Cancellare il file non è abbastanza, visto che si può ancora recuperare accedendo ai vecchi commit. Quello che bisogna fare è rimuovere il file da tutti i commit: $ git filter-branch --tree-filter 'rm file/segreto' HEAD Nella documentazione in *git help filter-branch* viene discusso questo esempio e dà anche un metodo più rapido. In generale, *filter-branch* vi permette di modificare intere sezioni della storia con un singolo comando. In seguito la cartella +.git/refs/original+ conterrà lo stato del vostro deposito prima dell'operazione. Verificate che il comando filter-branch abbia fatto quello che desiderate, e cancellate questa cartella se volete eseguire ulteriori comandi filter-branch. Infine rimpiazzate i cloni del vostro progetto con la versione revisionata se avete intenzione di interagire con loro più tardi. === Fare la storia === [[makinghistory]] Volete far migrare un progetto verso Git? Se è gestito con uno dei sistemi più diffusi, è molto probabile che qualcuno abbia già scritto uno script per esportare l'intera storia verso Git. Altrimenti, documentatevi sul comando *git fast-import* che legge un file di testo in un formato specifico per creare una storia Git a partire dal nulla. Tipicamente uno script che utilizza questo comando è uno script usa-e-getta scritto rapidamente e eseguito una volta sola per far migrare il progetto. Come esempio, incollate il testo seguente in un file temporaneo chiamato `/tmp/history`: ---------------------------------- commit refs/heads/master committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000 data <<EOT Commit iniziale EOT M 100644 inline hello.c data <<EOT #include <stdio.h> int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800 data <<EOT Remplacement de printf() par write(). EOT M 100644 inline hello.c data <<EOT #include <unistd.h> int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- Poi create un deposito Git a partire da questo file temporaneo eseguendo: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history Potete fare il checkout dell'ultima versione di questo progetto con: $ git checkout master . Il comando *git fast-export* può convertire qualsiasi deposito Git nel formato *git fast-import*, che vi permette di studiare come funzionano gli script di esportazione, e vi permette anche di convertire un deposito in un formato facilmente leggibile. Questi comandi permettono anche di inviare un deposito attraverso canali che accettano solo formato testo. === Dov'è che ho sbagliato? === Avete appena scoperto un bug in una funzionalità del vostro programma che siete sicuri funzionasse qualche mese fa. Argh! Da dove viene questo bug? Se solo aveste testato questa funzionalità durante lo sviluppo! Ma è troppo tardi. D'altra parte, a condizione di aver fatto dei commit abbastanza spesso, Git può identificare il problema. $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git estrae uno stato a metà strada di queste due versioni (HEAD e 1b6d). Testate la funzionalità e, se ancora non funziona: $ git bisect bad Altrimenti rimpiazzate "bad" con "good". Git vi trasporta a un nuovo stato a metà strada tra le versioni "buone" e quelle "cattive", riducendo così le possibilità. Dopo qualche iterazione, questa ricerca binaria vi condurrà al commit che ha causato problemi. Una volta che la vostra ricerca è finita, ritornate allo stato originario digitando: $ git bisect reset Invece di testare ogni cambiamento a mano, automatizzate la ricerca scrivendo: $ git bisect run my_script Git usa il valore di ritorno dello script 'my_script' che avete passato per decidere se un cambiamento è buono o cattivo: my_script deve terminare con il valore 0 quando una versione è ok, 125 quando deve essere ignorata, o un valore tra 1 e 127 se ha un bug. Un valore di ritorno negativo abbandona il comando bisect. Ma potete fare molto di più: la pagina di help spiega come visualizzare le bisezioni, esaminare o rivedere il log di bisect, e eliminare noti cambiamenti innocui per accelerare la ricerca. === Chi è che ha sbagliato? === Come in molti altri sistemi di controllo di versione, Git ha un comando per assegnare una colpa: $ git blame bug.c Questo comando annota ogni linea del file mostrando chi l'ha cambiata per ultimo e quando. A differenza di molti altri sistemi di controllo di versione, questa operazione è eseguita off-line, leggendo solo da disco locale. === Esperienza personale === In un sistema di controllo di versione centralizzato le modifiche della storia sono un'operazione difficile, che è solo disponibile agli amministratori. Creare un clone, una branch e fare un merge sono delle operazioni impossibili senza una connessione di rete. La stessa cosa vale per operazioni di base come ispezionare la storia, o fare il commit di un cambiamento. In alcuni sistemi, è necessaria una connessione di rete anche solo per vedere le proprie modifiche o per aprire un file con diritto di modifica. Sistemi centralizzati precludono il lavoro off-line, e necessitano infrastrutture di rete più ampie all'aumentare del numero di sviluppatori. Ancora più importante è il fatto che le operazioni sono a volte così lente da scoraggiare l'uso di alcune funzioni avanzate, a meno che non siano assolutamente necessarie. In casi estremi questo può valere addirittura per comandi di base. Quando gli utenti devono eseguire comandi lenti, la produttività viene compromessa per via delle continue interruzioni del flusso di lavoro. Ho sperimentato questi fenomeni personalmente. Git è stato il primo sistema di controllo di versione che ho utilizzato. Mi sono velocemente abituato al suo uso, dando per scontate molte funzionalità. Assumevo semplicemente che altri sistemi fossero simili: scegliere un sistema di controllo di versione non mi sembrava diverso da scegliere un editor di testo o un navigatore web. Sono rimasto molto sorpreso quando più tardi sono obbligato ad utilizzare un sistema centralizzato. Una connessione internet instabile ha poca importanza con Git, ma rende lo sviluppo quasi impossibile quando il sistema esige che sia tanto affidabile quanto il disco locale. Inoltre, mi sono trovato ad evitare l'uso di alcuni comandi per via delle latenze che comportavano, fatto che finalmente mi impediva di seguire il metodo di lavoro abituale. Quando dovevo eseguire un comando lento, le interruzioni influivano molto negativamente sulla mia concentrazione. Durante l'attesa della fine delle comunicazioni col server, facevo qualcos'altro per passare il tempo, come ad esempio controllare le email o scrivere della documentazione. Quando ritornavo al lavoro iniziale, il comando aveva terminato da tempo e mi ritrovare a dover cercare di ricordare che cosa stessi facendo. Gli esseri umani non sono bravi a passare da un contesto all'altro. C'erano anche interessanti effetti di tragedia dei beni comuni: prevedendo congestioni di rete, alcuni utenti consumavano più banda di rete che necessario per effettuare operazioni il cui scopo era di ridurre le loro attese future. Questi sforzi combinati risultavano ad aumentare ulteriormente le congestioni, incoraggiando a consumare ancora più larghezza di banda per cercare di evitare latenze sempre più lunghe. ������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/it/translate.txt������������������������������������������������������������������0000644�0001750�0001750�00000003120�12666307504�016146� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Appendice B: Tradurre questo manuale == La mia raccomandazione è di rispettare la seguente procedura per la traduzione di questo manuale, in maniera da poter rapidamente generare le versioni HTML e PDF del documento con gli script forniti, e così che tutte le traduzioni siano incorporate nello stesso deposito. Fate un clone della sorgente, poi create una directory il cui nome corrisponda http://www.iana.org/assignments/language-subtag-registry[al codice IETF della lingua desiderata] : vedere http://www.w3.org/International/articles/language-tags/Overview.en.php[l'articolo del W3C concernente internazionalizzazione]. Per esempio la versione in inglese è nella directory "en", quella in giapponese è in "ja". Nella nuova directory traducete i file +txt+ della directory originari "en". Per esempio, per creare questa guida in http://it.wikipedia.org/wiki/Lingua_klingon[Klingon], eseguite: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" è il codice IETF della lingua Klingon. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Tradurre il file. e così di seguito per tutti i file. Modificate il Makefile aggiungendo il codice della lingua alla variabile `TRANSLATIONS`. In questo modo potete rivedere il vostro lavoro in modo incrementale: $ make tlh $ firefox book-tlh/index.html Fate spesso dei commit per le vostre modifiche e avvertitemi non appena sono state implementate. Github possiede un'interfaccia che facilita il lavoro collaborativo: fate un fork del progetto "gitmagic", fate un push delle vostre modifiche, e chiedetemi di incorporarle. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/it/intro.txt����������������������������������������������������������������������0000644�0001750�0001750�00000017722�12666307504�015321� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Introduzione == Farò uso di un'analogia per introdurre il controllo di versione. Fate riferimento alla http://it.wikipedia.org/wiki/Controllo_versione[pagina wikipedia sul controllo di versione] per una spiegazione più sobria. === Il lavoro è gioco === Ho giocato ai videogiochi quasi tutta la mia vita. Per contro ho iniziato ad utilizzare sistemi di controllo di versione solo da adulto. Sospetto di non essere il solo, e il paragone tra i due può rendere alcuni concetti più facile da spiegare e comprendere. Pensate alla scrittura di codice o documenti come ad un videogioco. Non appena avete fatto progressi sostanziali, è desiderabile salvare il vostro lavoro. Per fare ciò cliccate sul pulsante 'Salva' del vostro fidato editor. Ma questo sovrascriverà la versione precedente del documento. È come quei vecchi videogiochi in cui si poteva salvare la partita, ma senza poter ritornare a uno stato precedente del gioco. Il che era un peccato, perché il vostro salvataggio precedente avrebbe potuto trovarsi ad un punto particolarmente divertente del gioco che avreste magari voluto rivisitare in futuro. O ancora peggio se il vostro salvataggio più recente si fosse rivelato essere uno stato da cui è impossibile vincere il gioco, obbligandovi a ricominciare la partita da zero. === Controllo di versione === Quando modificate un documento di cui volete conservare le vecchie versioni, potete 'Salvare come...' sotto un nome di file diverso, oppure copiare il file in un'altra cartella prima di salvarlo. Potreste anche comprimere queste copie del file, se volete risparmiare spazio su disco. Questa è una forma primitiva e inefficiente forma di controllo di versione. I videogiochi hanno migliorato questo punto molto tempo fa, provvedendo in molti casi multiple possibilità di salvataggio automaticamente ordinate temporalmente. Rendiamo il problema un po' più complicato. Immaginate di avere un un gruppo di file che vanno insieme, come il codice sorgente di un progetto o i file per un sito web. In questo caso se volete conservare una vecchia versione dovete archiviare una directory intera. Conservare diverse versioni a mano non è scomodo e diventa rapidamente impraticabile. Nel caso di alcuni videogiochi, il salvataggio di una partita consiste effettivamente in una directory contenente diversi file. Questi giochi nascondono questo dettaglio al giocatore e presentano una comoda interfaccia per gestire le diverse versioni di tale cartella. I sistemi di controllo di versione non sono niente più di questo. Hanno tutti una comoda interfaccia per gestire una directory piena di file. Potete salvare lo stato della directory di tanto in tanto, e più tardi potete caricare ognuno degli stati precedentemente salvati. A differenza della maggioranza di videogiochi, conservano in maniere intelligente lo spazio. Tipicamente, pochi file alla volta cambiano da una versione alla successiva. Si può quindi risparmiare spazio salvando le differenze invece di fare nuove copie complete. === Controllo distribuito === Immaginate ora un videogioco difficilissimo. Così difficile da terminare che molti esperti giocatori da tutto il mondo decidono di collaborare e condividere le loro partite salvate per cercare di venirne a capo. Gli http://it.wikipedia.org/wiki/Speedrun[Speedrun] sono un esempio concreto di questa pratica: dei giocatori si specializzano ognuno a giocare un livello dello stesso gioco nel miglior modo possibile, e collaborano così per ottenere dei risultati incredibili. Come costruireste un sistema che permetta loro di accedere facilmente ai salvataggi degli altri? E che permetta di caricarne di nuovi? Nel passato ogni progetto usava un sistema di controllo di versione centralizzato. Un server centrale unico da qualche parte manteneva tutte le partite salvate. Ogni giocatore conservava al massimo qualche salvataggio sul proprio computer. Quando un giocatore aveva intenzione di avanzare nel gioco, scaricava il salvataggio più recente dal server centrale, giocava per un po', salvava e ricaricava sul server i progressi ottenuti così che ognuno potesse usufruirne. Ma che cosa succedeva se un giocatore per qualche ragione voleva accedere ad una vecchia partita salvata? Forse perché la versione più attuale si trovava in uno stato da cui non era più possibile vincere il gioco perché qualcuno aveva dimenticato di raccogliere un oggetto al terzo livello, e ora era necessario ritrovare l'ultima partita salvata in un momento in cui la partita è ancora completabile. O magari si desiderava paragonare due partite salvate precedentemente per vedere quanto progresso avesse fatto un giocatore particolare. Potrebbero esserci molte ragioni per voler recuperare una vecchia versione, ma il risultato è sempre lo stesso: era necessario chiederla al server centrale. E più partite salvate erano necessarie, più dati era necessario trasmettere. La nuova generazione di sistemi di controllo di versione di cui Git fa parte sono detti sistemi distribuiti e possono essere pensati come una generalizzazione dei sistemi centralizzati. Quando i giocatori scaricano dal server centrale ricevono tutti i giochi salvati, non solo l'ultima. È come se fossero un http://it.wikipedia.org/wiki/Mirror_(informatica)[mirror] del server centrale. Questa operazione iniziale di clonaggio può essere costosa, soprattutto se c'è una lunga storia di salvataggi precedenti. Ma a lungo termine è una strategia che ripaga. Un beneficio immediato è che, quando per qualche ragione si desidera un salvataggio precedente, non è necessario comunicare con il server centrale. === Una sciocca superstizione === Una credenza popolare vuole che i sistemi distribuiti non siano adatti a progetti che richiedono un deposito centrale ufficiale. Niente potrebbe essere più lontano dalla verità. Fotografare qualcuno non ne ruba l'anima. Similarmente, clonare un deposito principale non ne diminuisce l'importanza. Una buona prima approssimazione è che tutto ciò che può fare un sistema di controllo di versione centralizzato può essere fatto meglio da un sistema distribuito ben concepito. Le risorse di rete sono semplicemente più costose che le risorse locali. Nonostante vedremo più in là che ci sono alcuni svantaggi associati agli approcci distribuiti, ci sono meno probabilità di fare paragoni sbagliate con questa approssimazione. Un piccolo progetto potrebbe non necessitare di tutte le funzionalità offerte da un tale sistema, ma il fatto di usare un sistema difficilmente estensibile per progetti piccoli è come usare il sistema di numerazione romano per calcoli con numeri piccoli. In aggiunta il vostro progetto potrebbe crescere al di là delle vostre previsioni iniziali. Usare Git dall'inizio è come avere sempre con se un coltellino svizzero, anche se lo utilizzate primariamente per aprire delle bottiglie. Quel giorno in cui avrete disperatamente bisogno un cacciavite sarete felici di avere più di un semplice apribottiglie. === Merge di conflitti === Per questo argomento la nostra analogia basata sui videogiochi inizia ad essere tirata per i capelli. Ritorniamo quindi invece al caso della formattazione di un documento. Immaginiamo che Alice inserisce una linea di codice all'inizio di un file, e Bob ne aggiunge una alla fine della propria copia. Entrambi caricano le loro modifiche nel deposito. La maggior parte dei sistemi decideranno una linea d'azione ragionevole: accettare e fondere (merge) entrambe le modifiche, così che sia le modifiche di Alice e Bob sono applicate. Ma supponiamo ora che Alice e Bob hanno fatto distinte modifiche alla stessa linea del documento. È impossibile procedere senza intervento umano. La seconda persona a caricare il file viene informata di un _conflitto di merge_, e bisogna scegliere una modifica piuttosto che un altra, oppure riscrivere interamente la riga. Situazioni più complesse possono presentarsi. Sistemi di controllo di versioni si possono occupare autonomamente dei casi più semplici, et lasciano i casi difficili all'intervento umano. Generalmente, questo comportamento è configurabile. ����������������������������������������������gitmagic-20160304/it/clone.txt����������������������������������������������������������������������0000644�0001750�0001750�00000027110�12666307504�015256� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Cloniamo == In vecchi sistemi di controllo di versione l'operazione standard per ottenere dei file era il checkout. Ottenete così un insieme di file corrispondenti a un particolare stato precedentemente salvato. In Git e altri sistemi distribuiti di controllo versione, l'operazione standard è il clonaggio. Per ottenere dei file si crea un 'clone' di tutto il deposito. In altre parole, diventate praticamente un http://it.wikipedia.org/wiki/Mirror_(informatica)[mirror] del server centrale. Tutto ciò che può fare il deposito centrale, potete farlo anche voi. === Sincronizzazione tra computers === Posso tollerare l'idea di creare degli archivi *tar* o di utilizzare *rsync* per backup di base. Ma a volte lavoro sul mio laptop, altre volte sul mio desktop, e può darsi che nel frattempo le due macchine non si siano parlate. Inizializzate un deposito Git e fate un *commit* dei vostri file su una macchina. Poi sull'altra eseguite: $ git clone altro.computer:/percorso/verso/il/file per creare una seconda copia dei file in un deposito Git. Da adesso in avanti, $ git commit -a $ git pull altro.computer:/percorso/verso/il/file HEAD trasferirà lo stato dei file sull'altro computer aggiornando quello su cui state lavorando. Se avete recentemente fatto delle modifiche conflittuali dello stesso file, Git ve lo segnalerà e dovrete ripetere nuovamente il commit, dopo che avrete risolto il conflitto. === Controllo classico di file sorgente === Inizializzate il deposito Git dei vostri file: $ git init $ git add . $ git commit -m "Commit iniziale" Sul server centrale inizializzate un 'deposito nudo' (*nudo* nella terminologia Git) in una cartella qualunque: $ mkdir proj.git $ cd proj.git $ git init --bare $ touch proj.git/git-daemon-export-ok Se necessario, lanciate il daemon: $ git daemon --detach # potrebbe già essere in esecuzione Per servizi di hosting Git, seguite le istruzioni per il setup del deposito Git che inizialmente sarà vuoto. Tipicamente bisognerà riempire un formulario in una pagina web. Trasferite il vostro progetto sul server centrale con: $ git push git://server.centrale/percorso/fino/a/proj.git HEAD Per ottenere i file sorgente, uno sviluppatore deve eseguire: $ git clone git://server.centrale/percorso/fino/a/proj.git Dopo aver fatto delle modifiche, lo sviluppatore le salva in locale: $ git commit -a Per aggiornare alla versione corrente: $ git pull Tutti i conflitti nel momento del merge devono essere risolti e validati: $ git commit -a Per inviare le modifiche locali al deposito centrale: $ git push Se il server principale ha nuove modifiche introdotte da altri sviluppatori, il push fallisce et lo sviluppatore deve aggiornarsi all'ultima versione, risolvere eventuali conflitti , e provare di nuovo. Perché i comandi pull e push precedenti funzionino bisogna avere accesso SSH. Comunque, chiunque può vedere il codice sorgente digitando: $ git clone git://server.centrale/percorso/fino/a/proj.git Il protocollo nativo git è come l'HTTP: non c'è nessuna autenticazione, così che tutti possono ottenere il progetto. Quindi, per default, push è proibito con protocollo git. === File sorgente segreti === Per un progetto chiuso, omettete il comando touch, e assicuratevi di mai creare un file chiamato `git-daemon-export-ok`. Il deposito in questo caso non potrà più essere ottenuto con il protocollo git; solo chi ha accesso SSH potrà vederlo. Se tutti i vostri deposito sono chiusi, lanciare il daemon git non è necessario perché la comunicazione avviene via SSH. === Depositi nudi === Un deposito nudo (*bare repository*) si chiama così perché non possiede una cartella di lavoro; contiene solo i file che sono solitamente nascosti nella sottocartella `.git`. In altre parole, mantiene unicamente la storia del progetto, e e non conserva nessuna versione. Un deposito nudo gioca un ruolo simile a quello di un server principale in un sistema di controllo di versione centralizzato: è dove è localizzato il vostro progetto. Altri sviluppatori clonano il nostro progetto da lì, e vi trasferiscono gli ultimi modifiche ufficiali. Tipicamente si trova su un server che non fa altro che distribuire dati. Lo sviluppo avviene nei cloni, così che il deposito principale non ha bisogno di una cartella di lavoro. Molti comandi git non funzionano per depositi nudi, a meno che la variabile globale `GIT_DIR` non viene definita con il percorso al deposito, o si utilizza l'opzione `--bare`. === Push vs pull === Perché abbiamo introdotto il comando `push`, invece di affidarci al più familiare comando `pull`? Prima di tutto il comando `pull` non funziona con depositi nudi: in questo caso bisogna invece usare `fetch`, un comando che discuteremo più tardi. Ma anche se avessimo un deposito normale sul server centrale, usare `pull` sarebbe sarebbe scomodo. Bisognerebbe per prima cosa connettersi al server e poi dare come argomento a `pull` l'indirizzo della macchina dalla quale vogliamo ottenere le modifiche. I firewalls potrebbero interferire nel processo, e cosa faremmo se non avessimo nemmeno accesso shell al server? In ogni caso, questo caso a parte, vi scoraggiamo l'uso di `push` per via della confusione che potrebbe generare quando la destinazione ha una cartella di lavoro. In conclusione, mentre state imparando ad usare Git, usate `push` solo se la destinazione è un deposito nudo; altrimenti usate `pull`. === Fare il forking di un progetto === Stufi del modo in cui un progetto è amministrato? Pensate che potreste fare un lavoro migliore? In questo caso, dal vostro server eseguite: $ git clone git://server.principale/percorso/verso/i/files Informate ora tutti del vostro fork del progetto sul vostro server. In seguito potete includere le modifiche provenenti dal progetto originale con: $ git pull === Il sistema definitivo di salvataggio === Volete degli archivi ridondanti e geograficamente distribuiti? Se il vostro progetto ha moti sviluppatori non c'è bisogno di fare niente! Ogni clone del vostro codice è effettivamente un backup. Non solo dello stato corrente, ma dell'intera storia del vostro progetto. Grazie al hashing crittografico, se qualcuno dovesse avere un clone corrotto, sarà individuato non appena si connetterà agli altri. Se il vostro progetto non è molto popolare, trovate il più alto numero possibile di server che possano ospitare dei cloni. Il vero paranoico dovrebbe anche sempre annotarsi l'ultimo codice SHA1 dell'HEAD di 20 bytes in un posto sicuro. Deve essere sicuro, non privato. Ad esempio, pubblicarlo in un giornale funzionerebbe bene, visto che sarebbe difficile realizzare un attacco modificando tutte le copie del giornale. === Multi-tasking alla velocità della luce === Immaginiamo di voler lavorare simultaneamente su diverse funzionalità. In questo caso fate un commit del progetto e eseguite: $ git clone . /una/nuova/cartella Grazie ai http://it.wikipedia.org/wiki/Collegamento_fisico[collegamenti fisici], i cloni locali richiedono meno tempo e spazio che i backup usuali. Potete ora lavorare simultaneamente su due funzionalità indipendentemente. Ad esempio, potete modificare un clone mentre l'altro sta compilando. Ad ogni modo, potete validare con 'commit' le vostre modifiche e importare con `pull` i cambiamenti dagli altri cloni: $ git pull /il/mio/altro/clone HEAD === Controllo di versione da battaglia === State lavorando ad un progetto che usa qualche altro sistema di controllo di versione, e vi manca disperatamente Git? In tal caso, inizializzate un deposito Git nella vostra cartella di lavoro: $ git init $ git add . $ git commit -m "Commit iniziale" poi clonatelo: $ git clone . /una/nuva/cartella Ora navigate alla nuova cartella e lavorate da qua, utilizzando Git come volete. Di tanto in tanto, quando volete sincronizzarvi con gli altri, recatevi nella cartella originale, sincronizzate utilizzando l'altro sistema di controllo di gestione, e poi digitate: $ git add . $ git commit -m "Sincronizzazione con gli altri" Andate quindi nella nuova cartella e lanciate: $ git commit -a -m "Descrizione delle mie modifiche" $ git pull La procedura per condividere le vostre modifiche con gli altri dipende d'altro canto dall'altro sistema di controllo di versione. La nuova cartella contiene i file con i vostri cambiamenti. Lanciate qualsiasi comando dell'altro sistema di controllo di gestione sia necessario per inviarli al deposito centrale. Subversion, che è forse il migliore sistema di gestione di versione centralizzato, è utilizzato da innumerevoli progetti. Il comando *git svn* automatizza la procedura precedente per i depositi Subversion, e può anche essere usato per esportare un progetto Git in un deposito Subversion. === Mercurial === Mercurial è un sistema di controllo di versione che può funzionare in tandem con Git in modo quasi trasparente. Con il plugin `hg-git` un utente di Mercurial può, senza svantaggi, inviare a (push) e ottenere (pull) da un deposito Git. Scaricate il plugin `hg-git` con Git: $ git clone git://github.com/schacon/hg-git.git o Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ Sfortunatamente, non sembra ci sia un plugin analogo per Git. Per questa ragione, mi sembra preferibile utilizzare Git piuttosto che Mercurial per i depositi principali. Nel caso di un progetto Mercurial di solito un volontario mantiene in parallelo un deposito Git che accomoda utenti Git, mentre, grazie al plugin `hg-git`, un progetto Git accomoda automaticamente utenti Mercurial. Nonostante il plugin può convertire un deposito Mercurial in uno Git trasferendolo in un deposito vuoto, questo è più facile con lo script `hg-fast-export.sh`, ottenibile da: $ git clone git://repo.or.cz/fast-export.git Per fare una conversione, in una nuovo cartella eseguite: $ git init $ hg-fast-export.sh -r /depot/hg dopo aver aggiunto lo script al vostro `$PATH`. === Bazaar === Menzioniamo brevemente Bazaar perché è il sistema di controllo di versione distribuito gratuito più popolare dopo Git e Mercurial. Bazaar ha il vantaggio del senno di poi, visto che è relativamente giovane; i suoi disegnatori hanno potuto imparare dagli errori commessi nel passato e evitare gli scogli storici. Inoltre, i suoi sviluppatori sono attenti a questioni come la portabilità e l'interoperabilità con altri sistemi di controllo di versione. Un plugin chiamato `bzr-git` permette agli utilizzatori di Bazaar di lavorare con depositi Git in una certa misura. Il programma `tailor` converte depositi Bazaar in depositi Git, e può farlo in maniera incrementale, mentre `bzr-fast-export` è fatto per le conversioni uniche. === Perché utilizzo Git === Ho originariamente scelto Git perché avevo sentito che era in grado di gestire l'inimmaginabilmente ingestibile sorgente del kernel Linux. Non ho mai sentito la necessità di cambiare. Git mi ha servito un servizio impeccabile, e non sono mai stato colto alla sprovvista dai suoi limiti. Siccome utilizzo primariamente Linux, i problemi che appaiono sulle altre piattaforme non mi concernono. In più preferisco programmi in C e scripts in bash rispetto agli eseguibili tipo gli scripts Python: ci sono meno dipendenze, e sono dipendente all'alta velocità di esecuzione. Ho riflettuto a come migliorare Git, arrivando fino al punto di scrivere la mia propria versione, ma solo come un esercizio accademico. Anche se avessi completato il mio progetto, sarei rimasto a Git comunque, visto che i vantaggi sarebbero stati minimi per giustificare l'utilizzazione di un sistema solitario. Naturalmente, i vostri bisogni e richieste probabilmente differiscono dai miei, e quindi potreste trovarvi meglio con un altro sistema. Nonostante ciò, non potete sbagliarvi scegliendo Git. ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/����������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12666307504�014106� 5����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/drawbacks.txt���������������������������������������������������������������0000644�0001750�0001750�00000017373�12666307504�016623� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Apêndice A: Deficiências do Git == Há algumas questões sobre o Git que joguei pra debaixo do tapete. Algumas são facilmente tratadas com script e gambiarras, algumas requerem uma reorganização ou redefinição do projeto, e para as poucas chateações remanescentes, só resta esperar por uma solução. Ou melhor ainda, solucione-as e ajude a todos! === Pontos fracos do SHA1 === A medida que o tempo passa, os criptógrafos descobrem mais e mais os pontos fracos do SHA1. Atualmente, encontrar colisões de hash é factível para algumas organizações bem equipadas. Dentro de alguns anos, talvez até um PC comum terá capacidade computacional suficiente para corromper silenciosamente um repositório Git. Esperamos que o Git irá migrar para uma função hash melhor antes que mais pesquisas destruam o SHA1. === Microsoft Windows === O Git no Microsoft Windows pode ser trabalhoso: - http://cygwin.com/[Cygwin], é um ambiente que deixa o Windows parecido com o Linux, tem http://cygwin.com/packages/git/[uma versão do Git para Windows]. - http://code.google.com/p/msysgit/[Git no MSys] é uma alternativa que requer suporte minimo para execução, embora alguns poucos comandos precisem ser mais trabalhados. === Arquivos Independentes === Se seu projeto é muito grande e tem muitos arquivos independentes que estão sendo constantemente modificados, o Git pode ser prejudicado mais do que os outros sistemas, pois os arquivos não são monitorados isoladamente. O Git monitora modificações no projeto como um todo, o que geralmente é benéfico. Uma solução é dividir seu projeto em pedaços, cada um composto de arquivos relacionados. Use *git submodule* se ainda quiser manter tudo num repositório só. === Quem Está Editando O Que? === Alguns sistemas de controle de versões irão forçá-lo a marcar explicitamente um arquivo de alguma maneira antes de editá-lo. Embora seja especialmente irritante quando isso envolve usar um servidor centralizado, isto tem dois benefícios: 1. Diff são rápido pois apenas os arquivos marcados são examinados; 2. Outros podem saber quem está trabalhando no arquivo perguntando ao servidor central quem marcou o arquivo para edição. Com o script certo, você pode fazer o mesmo com o Git. Isto requer apenas a cooperação dos programadores, que devem executar o script em particular quando estiver editando um arquivo. === Arquivo do Histórico === Como o Git armazena modificações muito amplas no projeto, reconstruir o histórico de um único arquivo requer mais trabalho do que em sistemas de controle de versões que monitoram arquivos individualmente. A penalidade é usualmente leve, e vale a pena devido à eficiência que dá as outras operações. Por exemplo, `git checkout` é tão rápido quanto `cp -a`, e os deltas que abrangem grandes partes do projeto tem uma compressão melhor do que os deltas de agrupamentos de arquivos. === Clone Inicial === A criação de um clone é mais trabalhosa do que fazer checkout em outros sistemas de controle de versões quando há um histórico grande. O custo inicial se paga a longo prazo, pois as futuras operações serão mais rápidas e offline. Entretanto, em algumas situações, é preferível criar um clone vazio com a opção `--depth`. Isto é muito mais rápido, porém resulta em um clone com funcionalidades reduzidas. === Projetos Voláteis === O Git foi feito para ser rápido no que diz respeito ao tamanho das mudanças. Humanos fazem poucas edições de uma versão para outra. É a correção de uma falha numa linha, uma nova característica do sistema, inclusão de comentário e assim por diante. Mas se seus arquivos diferem muito de uma versão para outra, em cada commit, seu histórico irá crescer acompanhando o tamanho do seu projeto todo. Não há nada que qualquer sistema de controle de versões possa fazer para ajudar, mas os usuários comuns do Git devem sofrer mais quando estiverem clonando históricos. As razões pelas quais as mudanças são tão grandes, devem ser analisadas. Talvez os formatos dos arquivos possam ser trocados. Edições menores só devem causar pequenas modificações em poucos arquivos. Ou talvez um banco de dados ou uma solução de backup/arquivamento seja o que você realmente precisa, e não um sistema de controle de versões. Por exemplo, um controle de versões pode não ser adequado para gerenciar fotos feitas periodicamente de uma webcam. Se os arquivos estão, realmente, mudando constantemente e precisam ser versionados, uma possibilidade é usar o Git de uma maneira centralizada. Pode-se criar clones vazios, que adiciona pouco ou quase nada ao histórico do projeto. É claro, que muitas ferramentas do Git não estarão disponíveis, e as correções devem ser enviadas como patch. Isto deve ser razoavelmente útil, para alguém que deseja manter um histórico de arquivos demasiadamente instáveis. Outro exemplo é um projeto dependente de firmware, o qual provavelmente estará em um grande arquivo binário. O histórico de arquivos de firmware é irrelevante para os usuários, e as atualizações têm uma péssima compressão, assim revisões de firmware estourarão o tamanho do repositório sem necessidade. Neste caso, o código fonte deve ser armazenado num repositório Git, e os arquivos binários mantidos separados do mesmo. Para facilitar o trabalho, alguém pode criar e distribuir um script que usa o Git para clonar o código e o faz um rsync ou um cria um clone vazio do Git para o firmware. === Contador Global === Alguns sistemas centralizados de controle de versões mantém um número inteiro positivo que é incrementado quando um novo commit é aceito. O Git referencia as modificações por seus hash, o que é o melhor na maioria das circunstâncias. Mas algumas pessoas gostariam de ter este número por perto. Felizmente, é fácil criar um script que faça isso a cada atualização, o repositório central do Git incrementa o número, talvez em uma marca (tag), e associa a mesma com o hash do último commit. Cada clone poderia gerenciar este contador, porém isto provavelmente seja desnecessário, já que apenas o contador do repositório central é que importará para todos. === Subdiretórios Vazios === Subdiretórios vazios não são monitorados. Crie arquivos vazios para resolver esse problema. A implementação atual do Git, e não seu o design, é a razão deste inconveniente. Com sorte, uma vez que o Git ganhe mais utilização, mais usuários devem clamar por esse recurso e ele poderá ser implementado. === Commit Inicial === Um cientista da computação tipico inicia uma contagem do 0, ao invés do 1. Entretanto, no que diz respeito a commit, o Git não segue esta convenção. Muito comandos são confusos antes do commit inicial. Além disso existem algumas arestas que precisam aparadas manualmente, seja com um rebase de um branch com um commit inicial diferente. O Git iria se beneficiar por definir o commit zero: assim que um repositório é construído, o HEAD deve ser definido para uma cadeia de 20 bytes zero. Esse commit especial representaria uma arvore vazia, sem pai, em um momento anterior a todos os repositórios Git Então executando um git log, por exemplo, deveria informar ao usuário que nenhum commit foi feito até agora, ao invés de terminar com um erro fatal. Da mesma maneira que as outras ferramentas. Cada commit inicial é implicitamente um decendente desse commit zero. Infelizmente existem alguns casos problemáticos. Se vários branch com commit iniciais diferentes forem merged juntos, então um rebase do resultado vai requerer uma intervenção manual substancial. === Peculiaridades da Interface === Para commit A e B, o significado da expressão "A..B" e "A...B" depende de onde o comando espera os dois pontos ou uma faixa. Veja *git help diff* e *git help rev-parse*. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/branch.txt������������������������������������������������������������������0000644�0001750�0001750�00000030516�12666307504�016111� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Bruxarias com branch == Ramificações (Branch) e mesclagens (merge) instantâneos são as características mais fantásticas do Git. *Problema*: Fatores externos inevitavelmente exigem mudanças de contexto. Um erro grave que se manifesta sem aviso, em uma versão já liberada. O prazo final é diminuído. Um desenvolvedor que o ajuda, em uma função chave do seu projeto, precisa sair. Em todos esses casos, você deixará de lado bruscamente o que esta fazendo e focará em uma tarefa completamente diferente. Interromper sua linha de pensamento provavelmente prejudicará sua produtividade, e quanto mais trabalhoso for trocar de contexto, maior será a perda. Com um controle de versões centralizado precisamos pegar uma nova cópia do servidor central. Sistemas distribuídos fazem melhor, já que podemos clonar o que quisermos localmente. Mais clonar ainda implica copiar todo o diretório de trabalho, bem como todo o histórico até o ponto determinado. Mesmo que o Git reduza o custo disso com o compartilhamento de arquivos e hardlinks, os arquivos do projeto devem ser recriados completamente no novo diretório de trabalho. *Solução*: O Git tem a melhor ferramenta para estas situações que é muito mais rápida e mais eficiente no uso de espaço do que a clonagem: *git branch*. Com esta palavra mágica, os arquivos em seu diretório de repente mudam de forma, de uma versão para outra. Esta transformação pode fazer mais do que apenas avançar ou retroceder no histórico. Seus arquivos podem mudar a partir da última liberação para a versão experimental, para a versão atualmente em desenvolvimento, ou para a versão dos seus amigos, etc. === A “tecla” chefe === Sempre joguei um desses jogos que ao apertar de um botão (“a tecla chefe”), a tela instantaneamente mudará para uma planilha ou algo mais sério. Assim se o chefe passar pelo seu escritório enquanto você estiver jogando, poderá rapidamente esconder o jogo. Em algum diretório: $ echo "I'm smarter than my boss" > myfile.txt $ git init $ git add . $ git commit -m "Initial commit" Criamos um repositório Git que rastreará um arquivo texto contendo uma certa mensagem. Agora digite: $ git checkout -b boss # nothing seems to change after this $ echo "My boss is smarter than me" > myfile.txt $ git commit -a -m "Another commit" ficou parecendo que nós sobrescrevemos nosso arquivo e fizemos um commit. Mas isto é um ilusão. Digite: $ git checkout master # switch to original version of the file e tcham tcham tcham! O arquivo texto foi restaurado. E se o chefe decidir bisbilhotar este diretório. Digite: $ git checkout boss # switch to version suitable for boss' eyes Você pode trocar entre as duas versões do arquivo quantas vezes quiser, e fazer commit independentes para cada uma. === Trabalho porco === [[branch]] Digamos que você está trabalhando em alguma função, e por alguma razão, precisa voltar 3 versões, e temporariamente colocar algumas declarações de controle para ver como algo funciona. Então: $ git commit -a $ git checkout HEAD~3 Agora você pode adicionar temporariamente código feio em qualquer lugar. Pode até fazer commit destas mudanças. Quando estiver tudo pronto, $ git checkout master para voltar para o trabalho original. Observe que qualquer mudança sem commit são temporárias. E se você desejasse salvar as mudanças temporárias depois de tudo? Fácil: $ git checkout -b dirty e faça commit antes de voltar ao branch master. Sempre que quiser voltar à sujeira, simplesmente digite: $ git checkout dirty Nós já falamos deste comando num capítulo anterior, quando discutimos carregamento de estados antigos salvos. Finalmente podemos contar toda a história: os arquivos mudam para o estado requisitado, porém saímos do branch master. Cada commit realizado a partir deste ponto nos seus arquivos o levarão em outra direção, que nomearemos mais adiante. Em outra palavras, depois de fazer checkout em um estado antigo, o Git automaticamente o colocará em um novo branch não identificado, que pode ser identificado e salvo com *git checkout -b*. === Correções rápidas === Você está fazendo algo quando mandam largar o que quer que seja e corrigir um erro recém-descoberto no commit `1b6d...`: $ git commit -a $ git checkout -b fixes 1b6d Então assim que tiver corrigido o erro: $ git commit -a -m "Bug fixed" $ git checkout master e volte a trabalhar no que estava fazendo anteriormente. Você pode até fazer um merge na correção realizada: $ git merge fixes === Merging === Com alguns sistemas de controle de versões, a criação de ramos (branching) é fácil mas realizar o merge de volta é dificil. Com o Git, o merge é tão trivial que você pode nem perceber que ele acontece. Nós, na realidade encontramos o merge há bastante tempo. O comando *pull* na realidade 'busca' ('fetches') um commit e faz um merge no ramo (branch) atual. Se você não tem nenhuma alteração local, então ele faz um merge rápido, que é um caso especial parecido a buscar a ultima versão em um sistema de controle de versões centralizado. Mas se você tem mudanças locais, o Git irá automaticamente fazer o merge, e reportar qualquer conflito. Comumente, um commit possui exatamente um 'commit pai', que recebe o nome de commit anterior. Fazer o merge de ramos (branches) juntos produz um commit com no mínimo dois pais. Isso levanta a questão: a que commit `HEAD~10` estamos nos referindo? Um commit pode ter vários pais, e qual deles vamos seguir? Acontece que essa notação escolhe o primeiro pai a cada vez. Isso é o desejável porque o ramo atual se torna o primeiro pai durante o merge; frequentemente você estará preocupado com as alterações que realizou no ramo atual, em oposição às alterações merged de outros ramos. Podemos referenciar um pai especifico com um circunflexo. Por exemplo, para mostrar os logs do segundo pai: $ git log HEAD^2 Podemos omitir o número para o primeiro pai. Por exemplo, para mostrar as diferenças com o primeiro pai: $ git diff HEAD^ Podemos combinar essa notação com outras. Por exemplo: $ git checkout 1b6d^^2~10 -b ancient inicia um novo ramo ``ancient'' representando o estado 10 commit atrás para o segundo pai do primeiro pai do commit iniciando com 1b6d. === Fluxo ininterrupto === Frequentemente em projetos de hardware, o segundo passo de um plano deve esperar que o primeiro passo termine. Um carro que esta esperando uma peça do fabricante deve permanecer na oficina até que a peça chegue. Um protótipo deve esperar até que o chip seja fabricado para que a montagem possa continuar. Os projetos de software pode ser semelhantes a isso. A segunda parte de uma nova função pode ter que esperar até que a primeira parte seja testada e liberada. Alguns projetos requerem que o código seja revisto antes de seu aceite, de modo que você deve esperar até que a primeira parte seja aprovada antes de iniciar a segunda parte. Graças à facilidade de realizar branch e merge, podemos “desviar” das regras e trabalhar na parte 2 antes da parte 1 estar oficialmente pronta. Suponha que fizemos o commit da parte 1 e enviamos para a revisão. Vamos dizer que estamos no ramo (branch) `master`. Então podemos ramificar: $ git checkout -b part2 Em seguida, trabalhamos na parte 2, fazendo commit das alterações durante o desenvolvimento. Mas errar é humano, e frequentemente você vai querer retornar e corrigir alguma coisa na parte 1. Se você estiver com sorte, ou for realmente bom, pode saltar esses comandos: $ git checkout master # Go back to Part I. $ fix_problem $ git commit -a # Commit the fixes. $ git checkout part2 # Go back to Part II. $ git merge master # Merge in those fixes. Eventualmente, quando a parte 1 for aprovada: $ git checkout master # Go back to Part I. $ submit files # Release to the world! $ git merge part2 # Merge in Part II. $ git branch -d part2 # Delete "part2" branch. Agora, você estará no ramo `master` novamente, com a parte 2 no diretório de trabalho. É fácil de estender esse truque para qualquer número de partes. É fácil também fazer branching retroativos: suponha que você percebeu tardiamente que deveria ter criado um branch 7 commits atrás. Então digite: $ git branch -m master part2 # Rename "master" branch to "part2". $ git branch master HEAD~7 # Create new "master", 7 commits upstream. O branch `master` agora contém somente a parte 1, e o branch `part2` contém todo o resto. Estamos no ultimo branch; criamos o `master` sem mudar para ele, porque queremos continuar a trabalhar na `part2`. Isso não é comum. Até agora, nós trocamos de ramos imediatamente após a sua criação, como em: $ git checkout HEAD~7 -b master # Create a branch, and switch to it. === Reorganizando uma Bagunça === Talvez você goste de trabalhar com todos os aspectos de um projeto num mesmo branch. E gostaria de manter seu trabalho para você mesmo e que os outros só vejam seus commit, apenas quando eles estiverem organizados. Inicie um par de branch: $ git branch sanitized # Create a branch for sanitized commits. $ git checkout -b medley # Create and switch to a branch to work in. A seguir, trabalhe em alguma coisa: corrigindo erros, adicionando funções, adicionando código temporário, e assim por diante, faça commit muitas vezes ao longo do caminho. Então: $ git checkout sanitized $ git cherry-pick medley^^ aplique o comit avô ao commit HEAD do ramo ``medley'' para o ramo ``sanitized''. Com os cherry-picks apropriados você pode construir um branch que contêm apenas código permanente, e tem os commit relacionados agrupados juntos. === Gerenciando os Branches === Para listar todos os branch, digite: $ git branch Por default, você deve iniciar em um branch chamado ``master''. Alguns defendem que o branch ``master'' deve permanecer intocado e que a seja criado novos branchs para suas próprias mudanças. As opções *-d* e *-m* permitem a você deletar ou mover (renomear) um branch. Veja *git help branch*. O branch ``master'' é uma personalização útil. Outros podem assumir que seu repositório possui um branch com esse nome e que ele contém a versão oficial de seu projeto. Embora possamos renomear ou apagar o branch ``master'', você deve procurar respeitar essa convenção. === Branches Temporários === Depois de um tempo você perceberá que está criando um branch de curta duração, frequentemente e por motivos parecidos: cada novo branch serve apenas para guardar o estado atual, assim você pode rapidamente voltar para estados antigos para corrigir um erro ou algo assim. É semelhante a mudar o canal da TV temporariamente para ver o que está passando nos outros canais. Mas, ao invés de apertar dois botões, você está criando, checando e apagando branchs temporários e seus commit. Felizmente, o Git tem um atalho que é tão conveniente como um controle remoto de TV: $ git stash Isto salva o estado atual num local temporário (um 'stash') e restaura o estado anterior. Seu diretório de trabalho parece ter voltado ao estado anteriormente salvo, e você pode corrigir erros, puxar as mudanças mais novas, e assim por diante. Quando quiser retornar ao estado anterior ao uso do stash, digite: $ git stash apply # You may need to resolve some conflicts. Você pode ter múltiplos stash, e manipulá-los de várias formas. Veja *git help stash*. Como deve ter adivinhado, o Git usa branch por traz dos panos para fazer este truque. === Trabalhe como quiser === Você pode se perguntar se os ramos valem a pena. Afinal, os clones são quase tão rápidos e você pode trocar entre eles com um comando cd ao invés de um comando esotérico do Git. Considere um navegador web. Por que suportar abas múltiplas bem como janelas múltiplas? É porque ao permitir ambas as características podemos acomodar uma variedade de estilos. Alguns usuários gostam de manter somente uma janela aberta do navegador, e utilizar varias abas para as páginas web. Outros preferem o outro extremo, várias janelas sem nenhuma aba. Outros podem preferir uma mistura dos estilos. Branching é como as abas para seu diretório de trabalho, e o clone é como uma nova janela do navegador. Essas operações são rápidas e locais, de modo que por que não experimentar para encontrar a combinação que melhor se adequa a você? O Git permite que você trabalhe exatamente do jeito que você desejar. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/secrets.txt�����������������������������������������������������������������0000644�0001750�0001750�00000031630�12666307504�016322� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Segredos Revelados == Vamos dar uma espiada sob o capô e explicar como o Git realiza seus milagres. Será uma explicação superficial. Para detalhes mais aprofundados consultar o http://schacon.github.com/git/user-manual.html[manual do usuário]. === Invisibilidade === Como pode o Git ser tão discreto? Fora ocasionais commit e merge, você pode trabalhar como se desconhecesse que existe um controle de versões. Isto é, até que precise dele, e é quando você ficará agradecido ao Git por estar vigiando o que faz o tempo todo. Outros sistemas de controle de versões não deixam você esquecê-los. As permissões dos arquivos são apenas de leitura, a menos que você diga ao servidor quais arquivos tem a intenção de editar. Os comandos mais básicos podem demorar muito quando o número de usuários aumenta. O trabalho pode ser interrompido quando a rede ou o servidor central para. Ao contrário, o Git guarda o histórico do seu projeto no diretório `.git` no diretório de trabalho. Essa é a sua cópia do histórico, de modo que você pode ficar offline até que deseje se comunicar com os outros. Você tem total controle sobre o destino de seus arquivos, pois o Git pode facilmente recriar um estado salvo a partir do `.git` a qualquer hora. === Integridade === A maioria das pessoas associam criptografia com manter informações secretas, mas outra aplicação igualmente importante é manter a integridade da informação. O uso correto das funções criptográficas de hash pode prevenir o corrupção acidental ou intencional dos dados. Um hash SHA1 pode ser entendido como um número identificador único de 160 bits para cada sequência de bytes que você vai encontrar na vida. Na verdade mais que isso: para cada sequência de bytes que qualquer ser humano jamais usará durante várias vidas. Como um hash SHA1 é, ele mesmo, uma sequência de bytes, podemos gerar um hash de sequências de bytes formada por outros hash. Essa observação simples é surpreendentemente útil: a procura por 'cadeias hash' ('hash chains'). Vamos ver mais adiante como o Git a utiliza para garantir com eficiência a integridade de dados. A grosso modo, o Git mantém os seus arquivos no subdiretório `.git/objects`, onde ao invés de ter arquivos com nomes “normais”, vamos encontrar somente identificadores (Ids). Utilizando os Ids como nomes de arquivos, bem como uns poucos lockfiles e alguns truques com o timestamp, o Git transforma qualquer sistema de arquivos simples em um poderoso banco de dados. === Inteligência === Como o Git sabe que um arquivo foi renomeado, mesmo que você nunca tenha mencionado o fato explicitamente? Com certeza, você executou *git mv*, mas isto é exatamente o mesmo que um *git rm* seguido por um *git add*. A análise heurística do Git verifica além das ações de renomear e de cópias sucessivas entre versões. De fato, ele pode detectar até pedaços de código sendo movidos ou copiados entre arquivos! Embora não cubra todos os casos, faz um trabalho decente, e esta característica está sendo sempre aprimorada. Caso não funcione com você, tente habilitar opções mais refinadas para detecção de cópias e considere uma atualização. === Indexando === Para cada arquivo monitorado, o Git armazena informações como: tamanho, hora de criação e última modificação, em um arquivo conhecido como 'index'. Para determinar se um arquivo foi modificado, o Git compara seus status atual com o que tem no index. Se coincidem, então ele pode ignorar o arquivo. Já que verificações de status são imensamente mais baratas que ler o conteúdo do arquivo, se você editar poucos arquivos, o Git vai atualizar seus status quase que instantaneamente. Falamos anteriormente que o index é uma área de atuação (staging). Por que um monte de status de arquivos é uma area de atuação (staging)? É porque o comando add coloca os arquivos no banco de dados do Git e atualiza esse status, enquanto o comando commit, sem opções, cria um commit baseado somente no status e arquivos já existentes no banco de dados. === Origem do Git === Esta http://lkml.org/lkml/2005/4/6/121[mensagem na lista de discussão do Linux Kernel] descreve a sequência de eventos que levaram ao Git. A discussão inteira é um sitio arqueológico fascinante para historiadores do Git. === O Banco de Dados de Objetos === Cada versão de seus dados é mantida em um 'bando de dados de objetos', que reside no subdiretório `.git/objects`: os outros residentes de `.git/` armazenam menos dados: o index, nomes dos branchs, etiquetas (tags), configurações de opções, logs, a localização do commit HEAD, e outros. O bando de dados objeto é elementar e elegante, e a origem do poder do Git. Cada arquivo dentro de `.git/objects` é um 'objeto'. Existem 3 tipos de objetos que nos interessam: objetos 'blob', objetos 'árvores' ('tree'), e objetos 'commit'. === Blobs === Primeiro, um truque mágico. Peque um nome de arquivo, qualquer arquivo. Em um diretório vazio, execute $ echo sweet > YOUR_FILENAME $ git init $ git add . $ find .git/objects -type f Voce verá +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. Como eu posso saber disso, sem saber o nome do arquivo? É por que o hash SHA1 de: "blob" SP "6" NUL "sweet" LF é aa823728ea7d592acc69b36875a482cdf3fd5c8d, onde SP é espaço, NUL é o byte zero e LF é um linefeed. Você pode verificar isso, digitando: $ printf "blob 6\000sweet\n" | sha1sum O Git é 'endereçável-por-conteúdo': os arquivos não são armazenados de acordo com seus nomes de arquivos, e sim pelo hash de seus dados, em um arquivo que chamamos de 'objeto blob' ('blob object'). Podemos pensar que o hash é um identificador único para o conteúdo do arquivo, de modo que estamos endereçando os arquivos pelo seu conteúdo. O `blob 6` inicial é meramente um header que consiste do tipo do objeto e seu tamanho em bytes; isso simplifica a organização interna. Assim eu poderia prever o que você irá ver. O nome do arquivo é irrelevante: somente os dados internos são utilizados para construir o objeto blob. Você pode estar se perguntando o que acontece com os arquivos idênticos. Tente adicionar cópias de seu arquivo, com qualquer nome de arquivo. O conteúdo do +.git/objects+ continua o mesmo não importa quantos você adiciona. O Git somente armazena o dado uma única vez. A propósito, os arquivos dentro de +.git/objects+ são comprimidos com a zlib de modo que você não consegue examiná-los diretamente. Faça uma filtragem por meio do http://www.zlib.net/zpipe.c[zpipe -d], ou digite: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d que mostra o objeto em um formato legível. === Árvores === Mas onde estão os nomes de arquivos? Eles precisam ser armazenados em algum lugar. Git fica sabendo o nome do arquivo durante um commit: $ git commit # Type some message. $ find .git/objects -type f Você pode ver agora 3 objetos. Dessa vez eu não consigo dizer quais os dois nomes de arquivos, já que eles dependem parcialmente do nome do arquivo que você escolheu. Vamos prosseguir assumindo que você escolheu ``rose''. Se não escolheu esse nome, você pode reescrever o histórico para ficar parecido com o que você fez: $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose' $ find .git/objects -type f Agora você poderá ver o arquivo +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, porque esse é o hash SHA 1 de seu conteudo: "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Verifique que este arquivo contém efetivamente o que falamos acima, digitando: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch Com o zpipe, é fácil verificar o hash: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum A verificação do hash é mais complicada via cat-file porque sua saida contem mais do que os dados descomprimidos do arquivo object. Esse arquivo é um objeto 'árvore' ('tree'): uma lista de tuplas que consiste em um tipo de arquivo, um nome de arquivo e um hash. Em nosso exemplo, este tipo de arquivo é 100644, que significa que `rose` é um arquivo normal, e o hash é o objeto blob que contém o termo `rose'. Outros tipos possíveis de arquivos são executáveis, symlinks e diretorios. No ultimo exemplo, o hash aponta para um objeto árvore. Se você executar o filter-branch, você obterá objetos antigos que não precisa mais. Embora sejam eliminados automaticamente quando o período de armazenamento expirar, iremos deletá-los agora para tornar o nosso exemplo mais fácil de seguir: $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune Para projetos reais você deve tipicamente evitar comandos como esses, já que eles destroem os backups. Se você deseja um repositório limpo, é geralmente melhor criar um novo clone. Também, tome cuidado quando manipular diretamente o +.git+: e se um comando Git esta executando ao mesmo tempo, ou uma falha na alimentação eletrica ocorre? Geralmente, as referências podem ser deletadas com *git update-ref -d*, embora seja mais seguro remover manualmente o +refs/original+. === Commits === Explicamos 2 dos 3 objetos. O terceiro é o objeto 'commit'. Seu conteúdo depende da mensagem de commit bem como da data e hora em que foi criado. Para combinar com o que temos aqui, vamos ter que fazer um pequeno truque: $ git commit --amend -m Shakespeare # Change the commit message. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Rig timestamps and authors. $ find .git/objects -type f Agora você pode ver +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ que é o hash SHA 1 de seu conteúdo: "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice <alice@example.com> 1234567890 -0800" LF "committer Bob <bob@example.com> 1234567890 -0800" LF LF "Shakespeare" LF Como anteriormente, você pode executar o zpipe ou cat-file para ver você mesmo. Esse é o primeiro commit, de modo que não existe nenhum commit pai, mas commit posteriores irão sempre conter no mínimo uma linha identificando o seu commit pai. === Indistinguível da Magia === O segredo do Git parece ser tão simples. Parece que você pode misturar um pouco de script shell e adicionar uma pitada de código C para cozinha-lo em questão de horas: uma mistura de operações básicas do sistema de arquivos e hash SHA 1, guarnecido com arquivos lock e fsyncs para robustez. De fato, isso descreve acuradamente as primeiras versões do Git. No entanto, além de truques engenhosos de empacotamento para economizar espaço, e truques engenhosos de indexação para economizar espaço, agora sabemos como o Git habilmente transforma um sistema de arquivos em um banco de dados perfeito para o controle de versões. Por exemplo, se algum arquivo dentro do banco de dados de objetos é corrompido por um erro de disco, então o seu hash não irá corresponder mais, alertando-nos sobre o problema. Fazendo hash de hash de outros objetos, mantemos a integridade em todos os níveis. Os commits são atomicos, isto é, um commit nunca pode armazenar parcialmente as mudanças: só podemos calcular o hash de um commit e armazenar ele no banco de dados após ter armazenado todas as árvores relevantes, blobs e os commits pais. O banco de dados de objetos é imune a interrupções inesperadas tais como falha de alimentação eletrica. Nós derrotamos até mesmo os adversários mais tortuosos. Suponha que alguem tente de maneira escondida, modificar o conteudo de um arquivo em uma versão antiga do projeto. Para manter o banco de dados dos objetos com uma aparência saudável, ele deve também alterar o hash do objeto blob correspondente, já que ele contem agora uma cadeia de bytes diferente. Isso significa que ele terá que alterar o hash de qualquer objeto árvore que referencia o arquivo, e em seguida alterar o hash de todos os objetos commit que estão envolvidos com esses objetos árvores, além dos hash de todos os descendentes desses commits. Isso significa que o hash do Head oficial será diferente do hash do repositório alterado. Seguindo a trilha dos hash não correspondentes podemos apontar o arquivo alterado, bem como o commit onde ele foi corrompido. Resumindo, graças aos 20 bytes que representam o ultimo commit seguro, é impossível adulterar um repositório Git. É sobre as famosas características do Git? Branching, Merging? Tags? Meros detalhes. O cabeçalho atual é mantido em um arquivo +.git/HEAD+, que contém um hash de um objeto commit. O hash é atualizado durante um commit bem como com muitos outros comandos. Branch são quase a mesma coisa: eles são arquivos em +.git/refs/heads+. Tags também: elas estão em +.git/refs/tags+ mas são atualizadas por um conjunto diferente de comandos. ��������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/multiplayer.txt�������������������������������������������������������������0000644�0001750�0001750�00000023020�12666307504�017213� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Git Multiplayer == Inicialmente, utilizei o Git em um projeto particular onde era o único desenvolvedor. Entre os comandos relacionados a natureza distribuída do Git, eu precisava somente do *pull* e *clone*, de modo a manter o mesmo projeto em vários lugares. Mais tarde, quis publicar meu código com o Git, e incluir as alterações dos contribuidores. Tive que aprender como gerenciar projetos com vários desenvolvedores de todas as partes do mundo. Felizmente, esse é o forte do Git, e indiscutivelmente sua razão de existir. === Quem sou eu? === Cada commit tem um nome de autor e e-mail, que pode ser mostrado pelo *git log*. O Git utiliza as configurações do sistema para preencher esses campos. Para fazer o preenchimento explícito, digite: $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com Omita o flag global para configurar essas opções somente para o repositório atual. === Git sob SSH, HTTP === Suponha que você tenha acesso SSH a um servidor web, mas que o Git não esteja instalado. Embora não tão eficiente quanto seu protocolo nativo, o Git pode se comunicar via HTTP. Baixe, compile e instale o Git em sua conta, e crie um repositório no seu diretório web: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update Para versões mais antigas do Git, o comando copy falha e você deve executar: $ chmod a+x hooks/post-update Agora você pode publicar suas últimas edições via SSH de qualquer clone: $ git push web.server:/path/to/proj.git master E qualquer um pode obter seu projeto com: $ git clone http://web.server/proj.git === Git sobre qualquer coisa === Deseja sincronizar os repositórios sem servidores, ou mesmo sem uma conexão de rede? Precisa improvisar durante uma emergência? Vimos que <<makinghistory, *git fast-export* e *git fast-import* podem converter repositórios para um único arquivo e vice-versa>>. Podemos enviar esses arquivos para outros lugares fazendo o transporte de repositórios git sob qualquer meio, mas uma ferramenta mais eficiente é o *git bundle*. O emissor cria um 'bundle': $ git bundle create somefile HEAD E então transporta o pacote, +somefile+, para outro lugar: por e-mail, pen-drive (ou mesmo uma saida impressa *xxd* e um scanner com ocr, sinais binários pelo telefone, sinais de fumaça, etc). O receptor recupera os commits do pacote executando: $ git pull somefile O receptor pode inclusive fazer isso em um diretório vazio. Apesar do seu tamanho, +somefile+ contém todo o repositório git original. Em grandes projetos, podemos eliminar o desperdício fazendo o empacotamento somente das mudanças que o outro repositório necessita. Por exemplo, suponha que o commit ``1b6d...'' é o commit mais recente compartilhado por ambas as partes: $ git bundle create somefile HEAD ^1b6d Se executado frequentemente, podemos esquecer que commit foi feito por último. A página de ajuda sugere utilizar tags para resolver esse problema. Isto é, após enviar um pacote, digite: $ git tag -f lastbundle HEAD e crie um novo pacote de atualização com: $ git bundle create newbundle HEAD ^lastbundle === Patches: A moeda Universal === Patches são representações textuais das suas mudanças que podem ser facilmente entendidas pelos computadores e pelos seres humanos. Isso tem um forte apelo. Você pode mandar um patch por e-mail para os desenvolvedores não importando que sistema de versão eles estão utilizando. Como os desenvolvedores podem ler o e-mail, eles podem ver o que você editou. Da mesma maneira, do seu lado, tudo o que você precisa é de uma conta de e-mail: não é necessário configurar um repositório online do Git. Lembrando do primeiro capítulo: $ git diff 1b6d > my.patch produz um patch que pode ser colado em um e-mail para discussão. Em um repositório Git, digite: $ git apply < my.patch para aplicar o patch. Em configurações mais formais, quando o nome do autor e talvez sua assinatura precisem ser armazenadas, podemos gerar os patches correspondentes a partir de um certo ponto com o comando: $ git format-patch 1b6d Os arquivos resultantes podem ser fornecidos ao *git-send-email*, ou enviados manualmente. Você também pode especificar uma faixa de commits: $ git format-patch 1b6d..HEAD^^ No lado do receptor, salve o e-mail como um arquivo, e entre com: $ git am < email.txt Isso aplica o patch de entrada e também cria um commit, incluindo as informações tais como o autor. Com um navegador cliente de e-mail, pode ser necessário clicar o botão para ver o e-mail em seu formato original antes de salvar o patch para um arquivo. Existem pequenas diferenças para os clientes de e-mail baseados em mbox, mas se você utiliza algum desses, provavelmente é o tipo de pessoa que pode resolver os problemas sem ler o manual. === Sinto muito, mas mudamos === Após fazer o clone de um repositório, executar o *git push* ou *git pull* irá automaticamente fazer o push ou pull da URL original. Como o Git faz isso? O segredo reside nas opções de configuração criadas com o clone. Vamos dar uma olhada: $ git config --list A opção +remote.origin.url+ controla a URL de origem; ``origin'' é um apelido dados ao repositório fonte. Da mesma maneira que a convenção do branch ``master'', podemos mudar ou deletar esse apelido mas geralmente não existe um motivo para fazê-lo. Se o repositório original for movido, podemos atualizar a URL via: $ git config remote.origin.url git://new.url/proj.git A opção +branch.master.merge+ especifica o branch remoto default em um *git pull*. Durante a clonagem inicial, ele é configurado para o branch atual do repositório fonte, mesmo que o HEAD do repositório fonte subsequentemente mova para um branch diferente, um pull posterior irá seguir fielmente o branch original. Essa opção somente aplica ao repositório que fizemos a clonagem em primeiro lugar, que é armazenado na opção +branch.master.remote+. Se fizermos um pull de outro repositório devemos estabelecer explicitamente que branch desejamos: $ git pull git://example.com/other.git master Isso explica por que alguns de nossos exemplos de pull e push não possuem argumentos. === Branches Remotos === Quando clonamos um repositório, clonamos também todos os seus branches. Voce pode não ter notado isso porque o Git sempre esconde os branches: mas podemos solicitá-los especificamente. Isso previne os branches no repositório remoto de interferir com seus branches, e também torna o Git mais fácil para os iniciantes. Liste os branches remotos com: $ git branch -r Voce deve obter uma saída como: origin/HEAD origin/master origin/experimental Eles representam branches e a HEAD de um repositório remoto, e pode ser utilizado em comandos normais do Git. Por exemplo, suponha que você fez vários commits, e gostaria de comparar contra a última versão buscada (fetched). Você pode buscar nos logs pelo hash apropriado SHA1, mas é muito mais fácil digitar: $ git diff origin/HEAD Ou você pode ver o que o branch ``experimental'' contém: $ git log origin/experimental === Remotos Múltiplos === Suponha que dois desenvolvedores estão trabalhando em nosso projeto, e que queremos manter o controle de ambos. Podemos seguir mais de um repositório a qualquer hora com: $ git remote add other git://example.com/some_repo.git $ git pull other some_branch Agora temos merged em um branch a partir do segundo repositório, e temos fácil acesso a todos os branches de todos os repositórios: $ git diff origin/experimental^ other/some_branch~5 Mas e se só queremos comparar as suas alterações sem afetar o trabalho de ambos? Em outras palavras, queremos examinar seus branches sem ter que suas alterações invadam nosso diretório de trabalho. Então ao invés de pull, execute: $ git fetch # Fetch from origin, the default. $ git fetch other # Fetch from the second programmer. Isso faz com que somente busque os históricos. Embora o diretório de trabalho continue intocado, podemos fazer referencia a qualquer branch de qualquer repositório em um comando Git pois agora possuímos uma copia local. Lembre-se de nos bastidores, um pull é simplesmente um *fetch* e um *merge*. Geralmente fazemos um *pull* pois queremos fazer um merge do ultimo commit após o fetch; essa situação é uma exceção notável. Veja *git help remote* para saber como remover repositórios remotos, ignorar certos branches e outras informações. === Minhas preferencias === Para meus projetos, eu gosto que contribuidores para preparar os repositórios a partir dos quais eu possa fazer um pull. Alguns serviços hospedeiros de Git permite que você hospede seu próprio fork de um projeto com o clique de um botão. Após ter buscado (fetch) uma árvore, eu executo comandos Git para navegar e examinar as alterações, que idealmente estão bem organizadas e bem descritas. Faço merge de minhas próprias alterações, e talvez faça edições posteriores. Uma vez satisfeito, eu faço um push para o repositório principal. Embora eu receba infrequentes contribuições, eu acredito que essa abordagem funciona bem. Veja http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[esse post do blog do Linus Torvalds]. Continuar no mundo Git é mais conveniente do que fazer patch de arquivos, já que ele me salva de convertê-los para commits Git. Além disso, o Git trata dos detalhes tais como registrar o nome do autor e seu endereço de e-mail, bem como o dia e hora, alem de pedir ao autor para descrever sua própria alteração. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/preface.txt�����������������������������������������������������������������0000644�0001750�0001750�00000007706�12666307504�016266� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������= Magia Git = Ben Lynn Agosto 2007 == Prefácio == http://git-scm.com/[Git] é um canivete suíço do controle de versões. Uma ferramenta polivalente realmente versátil, cuja extraordinária flexibilidade torna-o complicado de aprender, principalmente sozinho. Como Arthur C. Clarke observou, "Qualquer tecnologia suficientemente avançada é considerada mágica”. Esta é uma ótima forma de abordar o Git: novatos podem ignorar seu funcionamento interno e vê-lo como algo divertido que pode agradar aos amigos e enfurecer os inimigos com suas maravilhosas habilidades. Ao invés de entrar em detalhes, forneceremos apenas instruções para casos específicos. Após o uso repetido, você gradualmente entenderá como cada truque funciona, e como adaptar as receitas às suas necessidades. .Traduções - link:/\~blynn/gitmagic/intl/zh_cn/[Simplified Chinese]: by JunJie, Meng and JiangWei. Converted to link:/~blynn/gitmagic/intl/zh_tw/[Traditional Chinese] via +cconv -f UTF8-CN -t UTF8-TW+. - link:/~blynn/gitmagic/intl/fr/[French]: by Alexandre Garel, Paul Gaborit, and Nicolas Deram. Also hosted at http://tutoriels.itaapy.com/[itaapy]. - link:/~blynn/gitmagic/intl/de/[German]: by Benjamin Bellee and Armin Stebich; also http://gitmagic.lordofbikes.de/[hosted on Armin's website]. - link:/~blynn/gitmagic/intl/pt-BR/[Portuguese-Brazilian]: by J.I.Serafini and Leonardo Siqueira Rodrigues. - link:/~blynn/gitmagic/intl/ru/[Russian]: by Tikhon Tarnavsky, Mikhail Dymskov, and others. - link:/~blynn/gitmagic/intl/es/[Spanish]: by Rodrigo Toledo and Ariset Llerena Tapia. - link:/~blynn/gitmagic/intl/uk/[Ukrainian]: by Volodymyr Bodenchuk. - link:/~blynn/gitmagic/intl/vi/[Vietnamese]: by Trần Ngọc Quân; also http://vnwildman.users.sourceforge.net/gitmagic/[hosted on his website]. .Outras Edições - link:book.html[Single webpage]: barebones HTML, with no CSS. - link:book.pdf[PDF file]: printer-friendly. - http://packages.debian.org/gitmagic[Debian package], http://packages.ubuntu.com/gitmagic[Ubuntu package]: get a fast and local copy of this site. Handy http://csdcf.stanford.edu/status/[when this server is offline]. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Physical book [Amazon.com]]: 64 pages, 15.24cm x 22.86cm, black and white. Handy when there is no electricity. === Agradecimentos! === É com grande orgulho que vejo o grande número de pessoas que trabalho na tradução deste livro. Eu realmente agradeço pela grande audiência permitida pelos esforços dos tradutores citados a seguir. Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, Tyler Breisacher, Sonia Hamilton, Julian Haagsma, Romain Lespinasse, Sergey Litvinov, Oliver Ferrigni, David Toca, Сергей Сергеев, Joël Thieffry, e Baiju Muthukadan contribuiram com correções e melhorias. François Marier mantém o pacote Debian criado originalmente por Daniel Baumann. My gratitude goes to many others for your support and praise. I'm tempted to quote you here, but it might raise expectations to ridiculous heights. If I've left you out by mistake, please tell me or just send me a patch! === Licença === Este guia é regido pelos termos da http://www.gnu.org/licenses/gpl-3.0.html[a Licença Publica Geral GNU Versão 3]. Naturalmente, os fontes estão num repositório Git, e podem ser obtido digitando: $ git clone git://repo.or.cz/gitmagic.git # Cria um diretório "gitmagic". ou a partir de algum desses mirrors: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git $ git clone https://code.google.com/p/gitmagic/ $ git clone git://git.assembla.com/gitmagic.git $ git clone git@bitbucket.org:blynn/gitmagic.git GitHub, Assembla, e Bitbucket suportam repositórios privados, os dois últimos são grátis. ����������������������������������������������������������gitmagic-20160304/pt_br/basic.txt�������������������������������������������������������������������0000644�0001750�0001750�00000017427�12666307504�015743� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Truques básicos == Ao invés de se aprofundar no mar de comandos do Git, use estes exemplos elementares para dar os primeiros passos. Apesar de sua simplicidade, cada um deles são muito úteis. Na verdade, no meu primeiro mês com o Git, nunca precisei ir além das informações deste capítulo. === Salvando estados === Pensando em tentar algo mais arriscado? Antes de fazê-lo, tire uma “fotografia” de todos os arquivos do diretório atual com: $ git init $ git add . $ git commit -m "My first backup" Assim se algo der errado, você só precisará executar: $ git reset --hard Para salvar o estado novamente, faça: $ git commit -a -m "Another backup" === Adicionar, Remover, Renomear === Os comandos acima só irão verificar alterações nos arquivos que estavam presentes quando você executou seu primeiro *git add*. Se você adicionar novos arquivos ou diretórios terá que informar ao Git, com: $ git add readme.txt Documentation Do mesmo modo, se você quiser que o Git não verifique certos arquivos, faça: $ git rm kludge.h obsolete.c $ git rm -r incriminating/evidence/ O Git irá apagar estes arquivos, se você ainda não apagou. Renomear um arquivo é o mesmo que remover o nome antigo e adicionar um novo nome. Há também o atalho *git mv* que tem a mesma sintaxe do comando *mv*. Por exemplo: $ git mv bug.c feature.c === Desfazer/Refazer avançado === Às vezes, você só quer voltar e esquecer todas as mudanças realizadas a partir de um certo ponto, pois estão todos erradas. Então: $ git log mostrará uma lista dos últimos commit e seus hash SHA1: ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob <bob@example.com> Date: Tue Mar 14 01:59:26 2000 -0800 Replace printf() with write(). commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alice <alice@example.com> Date: Thu Jan 1 00:00:00 1970 +0000 Initial commit. ---------------------------------- Os primeiros caracteres do hash são suficientes para especificar o commit; alternativamente, copie e cole o hash completo. Digite: $ git reset --hard 766f para restaurar ao estado de um dado commit e apagar permanentemente os registros de todos os novos commit a partir deste ponto. Outras vezes você quer voltar, brevemente, para um estado. Neste caso, digite: $ git checkout 82f5 Isto levará você de volta no tempo, preservando os novos commit. Entretanto, como nas viagens no tempo dos filmes de ficção, se você editar e fizer um commit, você estará numa realidade alternativa, pois suas ações são diferentes das realizadas da primeira vez. Esta realidade alternativa é chamada de branch, nós falaremos mais sobre isso depois. Por ora, apenas lembre-se que: $ git checkout master lhe levará de volta para o presente. Assim faça o Git parar de reclamar, sempre faça um commit ou reset em suas alterações antes de executar um checkout. Voltemos para a analogia dos jogos de computador : - *`git reset --hard`*: carrega um salvamento antigo e apaga todos jogos salvos mais novos do que este que foi carregado. - *`git checkout`*: carrega um salvamento antigo, mas se jogar a partir dele, os próximos salvamento realizados se desvincularão dos salvamentos já realizados após o que foi carregado. Qualquer jogo salvo que você fizer será colocado em um branch separado representado uma realidade alternativa em que entrou. Lidaremos com isso mais adiante. <<branch,We deal with this later>>. Você pode escolher restaurar apenas alguns arquivos ou diretórios acrescentando-os ao final do comando: $ git checkout 82f5 some.file another.file Tome cuidado, pois esta forma de *checkout* pode sobrescrever arquivos sem avisar. Para prevenir acidentes, faça um commit antes de qualquer comando checkout, especialmente quando esta aprendendo a utilizar o Git. De modo geral, sempre que se sentir inseguro sobre alguma operação, seja um comando Git ou não, execute primeiro o comando *git commit -a*. Não gosta de copiar e colar hash? Então use: $ git checkout :/"My first b" para ir ao commit que começa a frase informada. Você também pode solicitar pelo estado salvo ha 5 commit atrás: $ git checkout master~5 === Revertendo === Como num tribunal, eventos podem ser retirados dos registros. Da mesma maneira, você pode especificar qual commit desfazer. $ git commit -a $ git revert 1b6d irá desfazer apenas o commit do hash informado. A regressão é gravada como um novo commit, e que pode ser confirmada executando o comando *git log*. === Geração do Changelog === Alguns projetos precisam de um http://en.wikipedia.org/wiki/Changelog[changelog]. Podemos gerar um changelog com o comando: $ git log > ChangeLog === Download de arquivos === Para obter uma cópia de um projeto gerenciado com GIT digite: $ git clone git://server/path/to/files Por exemplo, para obter todos os arquivos usados para criar este site: $ git clone git://git.or.cz/gitmagic.git Mais adiante, teremos muito o que dizer sobre o comando *clone*. === A Última Versão === Se você já obteve a cópia de um projeto usando *git clone*, pode agora atualizar para a última versão com: $ git pull === Publicação instantânea === Suponha que você tenha escrito um script e gostaria de compartilhá-lo. Você poderia simplesmente dizer para pegarem do seu computador, mas se o fizerem enquanto você está melhorando o script ou experimentando algumas mudanças, eles podem ter problemas. Obviamente, é por isso que existem ciclos de liberação. Desenvolvedores podem trabalhar num projeto com frequência, mas só disponibilizam o código quando sentem que o mesmo está apresentável. Para fazer isso com Git, no diretório onde está seu script, execute: $ git init $ git add . $ git commit -m "First release" Então avise aos outros para executarem: $ git clone your.computer:/path/to/script para obter seu script. Assume-se que eles têm acesso ssh. Se não, execute *git daemon* e avise-os para executar: $ git clone git://your.computer/path/to/script A partir de agora, toda vez que seu script estiver pronto para liberar, execute: $ git commit -a -m "Next release" e seu usuários podem atualizar suas versões, indo para o diretório que contém seu script, e executando: $ git pull Seu usuários nunca ficarão com uma versão do seu script que você não queira. Obviamente este truque serve para tudo, não apenas script. === O que eu fiz? === Saiba quais as mudanças que você fez desde o último commit com: $ git diff Ou desde ontem: $ git diff "@{yesterday}" Ou entre uma versão particular e duas versões atrás: $ git diff 1b6d "master~2" Em cada um dos exemplos, a saída será um patch que pode ser aplicado com o *git apply*. Tente também: $ git whatchanged --since="2 weeks ago" Às vezes navego pelo histórico com o http://sourceforge.net/projects/qgit[qgit], em razão de sua interface mais fotogênica, ou com o http://jonas.nitro.dk/tig/[tig], uma interface em modo texto ótima para conexões lentas. Alternativamente, instale um servidor web, e execute git instaweb e use um navegador. === Exercícios === Seja A, B, C D quatro commits sucessivos onde B é idêntico a A exceto por alguns arquivos que foram removidos. Queremos adicionar novamente os arquivos em D. Como podemos fazer isso? Existem no minimo 3 soluções. Assumindo que estamos em D. 1. A diferença entre A e B são or arquivos removidos. Podemos criar um patch representando esta diferença e aplicá-la: $ git diff B A | git apply 2. Como salvamos os arquivos em A, podemos recuperá-los: $ git checkout A foo.c bar.h 3. Podemos visualizar as mudanças de A para B que queremos desfazer: $ git revert B Qual a opção é melhor? A que você preferir, É fácil fazer o que você quer com o git, e na maioria das vezes existe mais de uma forma de fazê-lo. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/grandmaster.txt�������������������������������������������������������������0000644�0001750�0001750�00000026135�12666307504�017165� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Grão-Mestre Git == Até agora, você deve ser capaz de navegar pelas páginas do *git help* e entender quase tudo. Entretanto, identificar o comando exato para resolver um dados problema pode ser tedioso. Talvez possa economizar algum tempo seu: a seguir estão algumas receitas que precisei no passado. === Disponibilização de Código === Para meus projetos, o Git organiza exatamente os arquivos que quero guardar e disponibilizar para os usuários. Para criar um tarball do código fonte, executo: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === Commit do que Mudou === Mostrar ao Git quando adicionamos, apagamos e/ou renomeamos arquivos pode ser problemático em alguns projetos. Em vez disso, você pode digitar: $ git add . $ git add -u O Git analisará os arquivos no diretório atual e trabalhar nos detalhes, automaticamente. No lugar do segundo comando add, execute `git commit -a` se sua intenção é efetuar um commit neste momento. Veja *git help ignore* para saber como especificar os arquivos que devem ser ignorados. Você pode executar isto em apenas um passo com: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove As opções *-z* e *-0* previnem contra os transtornos de arquivos com caracteres estranhos no nome. Note que este comando adiciona os arquivos ignorados. Logo, você pode querer usar as opções `-x` ou `-X`. === Meu Commit é muito Grande! === Você esqueceu de fazer commit por um muito tempo? Ficou codificando furiosamente e esqueceu do controle de versões até agora? Fez uma série de modificações não relacionadas entre si, pois este é seu estilo? Não se preocupe. Execute: $ git add -p Para cada modificação realizada, o Git mostrará o pedaço do código alterado, e perguntará se ele deve fazer parte do próximo commit. Responda "y" (sim) ou "n" (não). Há outras opções, como o adiamento dessa decisão; digite "?" para aprender como. Uma vez satisfeito, digite: $ git commit para fazer um commit exatamente com as modificações aprovadas ('staged'). Lembre-se de retirar a opção *-a*, caso contrário o commit conterá todas as modificações. E se você tiver editado vários arquivos em vários locais? Rever cada modificação uma por uma será frustante e enfadonho. Neste caso, use *git add -i*, cuja interface é menos simples, porém mais flexível. Com algumas poucas teclas, você pode aprovar ou não vários arquivos de uma vez, ou rever e selecionar as modificações em um arquivo especifico. Alternativamente, execute *git commit \--interactive* o que automaticamente efetuará seus commit assim que terminar de aprovar. === O Indice: A Área de Atuação do Git === Até agora estivemos evitando o famoso 'index' do git, mas agora teremos que enfrentá-lo para explicar o tópico acima. O index é uma área de atuação temporária. O Git frequentemente atualiza os dados diretamente entre seu projeto e sua historia. Preferencialmente, Git primeiro armazena os dados no index, e então copia os dados do index para seu destino final. Por exemplo, *commit -a* é na realidade um processo em duas etapas. A primeira etapa coloca uma “fotografia” do estado atual de cada arquivo rastreado em um índice. O segundo passo registra permanentemente a “fotografia” localizado no índice. O commit sem a opção *-a* somente executa o segundo passo, e somente faz sentido após a execução de um comando que de alguma maneira altera o índice, tal como o *git add*. Geralmente podemos ignorar o index e supor que estamos lendo e escrevendo diretamente no histórico. Nessas ocasiões, queremos um controle mais fino, de modo que manipulamos o índice. Colocamos uma “fotografia” de algumas, mas não todas, as nossas alterações no índice, e então registramos permanentemente esta “fotografia” cuidadosamente manipulada. === Não perca a CABEÇA (HEAD) === A etiqueta HEAD (cabeçalho) é como um indicador que normalmente aponta para o último commit, avançando a cada novo commit. Alguns comandos do Git permitem movê-la. Por exemplo: $ git reset HEAD~3 irá mover o HEAD três commit para trás. Assim todos os comandos do Git passam a agir como se você não tivesse realizados os últimos três commit, enquanto seus arquivos permanecem no presente. Consulte a página do manual para mais aplicações. Mas como se faz para voltar para o futuro? Os últimos commit não sabem do futuro. Se você tem o SHA1 do HEAD original então: $ git reset 1b6d Mas suponha que você não tenha anotado. Não se preocupe, para comandos desse tipo, o Git salva o HEAD original com uma etiqueta chamada de ORIG_HEAD, e você pode retornar são e salvo com: $ git reset ORIG_HEAD === Explorando o HEAD === Talvez ORIG_HEAD não seja suficiente. Talvez você só tenha percebido que fez um erro descomunal e precisa voltar para um antigo commit de um branch há muito esquecido. Por default, o Git mantém um commit por pelo menos duas semanas, mesmo se você mandou o Git destruir o branch que o contêm. O problema é achar o hash certo. Você pode procurar por todos os valores de hash em `.git/objects` e por tentativa e erro encontrar o que procura. Mas há um modo mais fácil. O Git guarda o hash de todos os commit que ele calcula em `.git/logs`. O sub-diretório refs contêm o histórico de toda atividade em todos os branch, enquanto o arquivo `HEAD` mostra todos os valores de hash que teve. Este último pode ser usado para encontrar o hash de um commit num branch que tenha sido acidentalmente apagado. O comando reflog fornece uma interface amigável para estes arquivos de log. Experimente $ git reflog Ao invés de copiar e colar o hash do reflog, tente: $ git checkout "@{10 minutes ago}" Ou faça um checkout do quinto último commit com: $ git checkout "@{5}" Leia a seção ``Specifying Revisions'' da ajuda com *git help rev-parse* para mais informações. Você pode querer configurar um período mais longo de carência para condenar um commit. Por exemplo: $ git config gc.pruneexpire "30 days" significa que um commit apagado será permanentemente eliminado após passados 30 dias e executado o comando *git gc*. Você também pode desativar as execuções automáticas do *git gc*: $ git config gc.auto 0 assim os commit só serão permanentemente eliminados quando executado o *git gc* manualmente. === Baseando se no Git === Seguindo o jeito UNIX de ser, o Git permite ser facilmente utilizado como um componente de “baixo nível” para outros programas, tais como interfaces GUI, interfaces web, interfaces alternativas de linha de comando, ferramentas de gerenciamento de patchs, ferramentas de importação e conversão e outras. Na realidade, alguns comandos Git são eles mesmos, scripts apoiados em ombros de gigantes. Com pouco trabalho, você pode configurar o Git para suas preferencias. Um truque simples é criar alias (abreviações), internas ao Git para abreviar os comandos utilizados mais frequentemente: $ git config --global alias.co checkout $ git config --global --get-regexp alias # display current aliases alias.co checkout $ git co foo # same as 'git checkout foo' Outra é imprimir o branch atual no prompt, ou no título da janela. É só executar: $ git symbolic-ref HEAD que mostra o nome do branch atual. Na prática, muito provavelmente você não quer ver o "refs/heads/" e ignorar os erros: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- O subdiretório +contrib+ é um baú do tesouro de ferramentas construídas com o Git. Com o tempo algumas delas devem ser promovidas para comandos oficiais. Nos sistemas Debian e Ubuntu esse diretório esta em +/usr/share/doc/git-core/contrib+. Um residente popular é +workdir/git-new-workdir+. Por meio de um inteligente link simbólico, este script cria um novo diretório de trabalho cujo histórico é compartilhado com o repositório original: $ git-new-workdir an/existing/repo new/directory O novo diretório e arquivos dentro dele podem ser vistos como um clone, exceto que como o histórico é compartilhado, as duas arvores automaticamente estarão em sincronia. Não é necessário fazer merge, push ou pull. === Manobras Radicais === As versões recentes do Git tornaram mais difícil para o usuário destruir acidentalmente um dado. Mas se você sabe o que esta fazendo, você pode transpor estas salvaguardas utilizadas nos comandos mais comuns. *Checkout*: Se há modificações sem commit, um checkout simples falhará. Para destruir estas modificações, e fazer um checkout de um certo commit assim mesmo, use a opção force (-f): $ git checkout -f HEAD^ Por outro lado, se for especificado algum endereço em particular para o checkout, então não haverá checagem de segurança. O endereço fornecido será silenciosamente sobrescrito. Tenha cuidado se você usa o checkout desse jeito. *Reset*: O Reset também falha na presença de modificações sem commit. Para obrigá-lo, execute: $ git reset --hard 1b6d *Branch*: Apagar um branch falha se isto levar a perda das modificações. Para forçar, digite: $ git branch -D dead_branch # instead of -d Analogamente, a tentativa de sobrescrever um branch movendo-o falha for causar a perda de dados. Para forçar a movimentação do branch, use: $ git branch -M source target # instead of -m Ao contrário do checkout e do reset, estes dois comandos adiarão a destruição dos dados. As modificações ainda serão armazenadas no subdiretório .git, e podem ser resgatados, recuperando o hash apropriado do `.git/logs` (veja a seção "Explorando o HEAD" acima). Por padrão, eles serão mantidos por pelo menos duas semanas. *Clean*: Alguns comandos do Git recusam-se a avançar devido o receio de sobrescrever arquivos não “monitorados” (sem commit). Se você tiver certeza de que todos os arquivos e diretórios não monitorados são dispensáveis, então apague-os sem misericórdia com: $ git clean -f -d Da próxima vez, o maldito comando não se recusará a funcionar! === Prevenção a maus Commits === Erros estúpidos poluem meus repositórios. Os mais assustadores são os arquivos perdidos devido a comandos *git add* esquecidos. Transgressões mais leves são espaço em branco e conflitos de merge não resolvidos: embora sem perigo, eu gostaria que eles nunca aparecessem nos meus registros públicos. Se eu tivesse comprado um seguro idiota usando _hook_ para me alertar sobre esses problemas: $ cd .git/hooks $ cp pre-commit.sample pre-commit # Older Git versions: chmod +x pre-commit Agora o Git aborta um commit se espaço em branco sem utilidade ou um conflito de merge não resolvido for detectado. Para este guia, eu eventualmente adiciono o seguinte ao inicio do hook *pre-commit* para proteção contra as “mentes sem noção”. if git ls-files -o | grep '\.txt$'; then echo FAIL! Untracked .txt files. exit 1 fi Várias operações do Git suportam o hook: veja *git help hooks*. Nós ativamos o hook de exemplo *post-update* anteriormente quando discutimos o Git sob HTTP. Isso executa sempre que o HEAD se movimenta. O script de exemplo post-update atualiza os arquivos que o Git necessita para a comunicação sob transportes agnósticos do Git, como o HTTP. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/history.txt�����������������������������������������������������������������0000644�0001750�0001750�00000027312�12666307504�016355� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Lições de historia == Uma consequência da natureza distribuída do Git é que o histórico pode ser editado facilmente. Mas se você adulterar o passado, tenha cuidado: apenas rescreva a parte do histórico que só você possui. Assim como as nações sempre argumentam sobre quem comete atrocidades, se alguém tiver um clone cuja versão do histórico seja diferente do seu, você pode ter problemas para conciliar suas árvores quando interagirem. Alguns desenvolvedores acreditam que o histórico deva ser imutável, com falhas ou não. Outros, acreditam que suas árvores devem estar apresentáveis antes de liberá-las ao público. O Git contempla ambos pontos de vista. Tal como a clonagem, branch e merge, rescrever o histórico é simplesmente outro poder que o Git lhe concede. Cabe a você a usá-lo sabiamente. === Eu corrijo === Acabou de fazer um commit, mas queria ter escrito uma mensagem diferente? Então execute: $ git commit --amend para mudar a última mensagem. Percebeu que esqueceu de adicionar um arquivo? Execute *git add* para adicioná-lo, e então execute o comando acima. Quer incluir mais algumas modificações no último commit? Faça-as e então execute: $ git commit --amend -a === ... e tem mais === Suponha que o problema anterior é dez vezes pior. Após uma longa sessão onde você fez um monte de commit. E você não está muito satisfeito com a organização deles, e algumas das mensagens dos commit poderiam ser reformuladas. Então execute: $ git rebase -i HEAD~10 e os últimos 10 commit aparecerão em seu $EDITOR favorito. Trecho de exemplo: pick 5c6eb73 Added repo.or.cz link pick a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile Os commit antigos precedem os mais novos nessa lista, diferentemente do comando `log`. Aqui, 5c6eb73 é o commit mais velho e o 100834f é o commit mais novo. Então: - Remova os commit deletando as linhas. Como o comando revert, mas sem fazer o registro: será como se o commit nunca tenha existido. - Reorganize os commit reorganizando as linhas. - Substitua `pick` com: * `edit` para modificar a mensagem do commit; * `reword` para alterar a mensagem de log; * `squash` para fazer o merge de um commit com o commit anterior; * `fixup` para fazer o merge de um commit com o anterior e descartar a mensagem de log. Por exemplo, queremos substituir o segundo `pick` por `squash`: pick 5c6eb73 Added repo.or.cz link squash a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile Após salvar e sair, o Git ira fazer o merge a311a64 com o 5c6eb73. Assim *squash* faz o merge no próximo commit: pense como ``squash up''. O Git, então combina suas mensagens de logs e apresenta para edição. O comando *fixup* salta essa etapa; a mensagem de log squashed é simplesmente descartada. Se marcar um commit com *edit*, o Git retorna você ao passado, ao commit mais velho. Você pode emendar o commit velho como descrito na seção anterior, e até mesmo criar um novo commit que pertença a esse. Uma vez que esteja satisfeito com o ``retcon'', siga adiante executando: $ git rebase --continue O Git reenvia os commits até o próximo *edit*, ou ao presente se não restar nenhum. Você pode também, abandonar o rebase com: $ git rebase --abort Portanto, faça commit cedo e com frequência: e arrume tudo facilmente mais tarde com um rebase. === Alterações locais por último === Você está trabalhando em um projeto ativo. Faz alguns commit locais ao longo do tempo, e sincroniza com a árvore oficial com merge. Este ciclo se repete algumas vezes até estar tudo pronto para ser enviado à árvore central. Mas agora o histórico no seu clone local está uma confusão com o emaranhado de modificações locais e oficiais. Você gostaria de ver todas as suas modificações em uma seção contínua e depois todas as modificações oficiais. Este é um trabalho para *git rebase* conforme descrito acima. Em muitos casos pode-se usar a opção *--onto* e evitar sua interação. Veja também *git help rebase* com exemplos detalhados deste incrível comando. Você pode dividir commit. Ou até reorganizar branch de uma árvore. Tome cuidado: o comando rebase é muito poderoso. Para rebases complicados, primeiro faça um backup com *git clone*. === Reescrevendo o histórico === Eventualmente, será necessário que seu controle de código tenha algo equivalente ao modo Stanlinesco de retirada de pessoas das fotos oficiais, apagando-os da história. Por exemplo, suponha que temos a intenção de lançar um projeto, mas este envolve um arquivo que deve ser mantido privado por algum motivo. Talvez eu deixe meu número do cartão de crédito num arquivo texto e acidentalmente adicione-o ao projeto. Apagá-lo é insuficiente, pois, pode ser acessado pelos commit anteriores. Temos que remover o arquivo de todos os commit: $ git filter-branch --tree-filter 'rm top/secret/file' HEAD Veja *git help filter-branch*, que discute este exemplo e mostra um método mais rápido. No geral, *filter-branch* permite que você altere grandes seções do histórico só com um comando. Depois, o diretório +.git/refs/original+ descreve o estado dos casos antes da operação. Verifique se o comando filter-branch faz o que você deseja, e então apague esse diretório se você deseja executar mais comandos filter-branch. Por ultimo, você deve substituir os clones do seu projeto pela versão revisada se desejar interagir com eles depois. === Fazendo história === [[makinghistory]] Quer migrar um projeto para Git? Se ele for gerenciado por um algum dos sistemas mais conhecidos, então é possível que alguém já tenha escrito um script para exportar todo o histórico para o Git. Caso contrário, dê uma olhada em *git fast-import*, que lê um texto num formato especifico para criar o histórico Git do zero. Normalmente um script usando este comando é feito as pressas sem muita frescura e é executado apenas uma vez, migrando o projeto de uma só vez. Por exemplo, cole a listagem a seguir num arquivo temporário, como `/tmp/history`: ---------------------------------- commit refs/heads/master committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000 data <<EOT Initial commit. EOT M 100644 inline hello.c data <<EOT #include <stdio.h> int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800 data <<EOT Replace printf() with write(). EOT M 100644 inline hello.c data <<EOT #include <unistd.h> int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- Em seguida crie um repositório Git a partir deste arquivo temporário digitando: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history Faça um checkout da última versão do projeto com: $ git checkout master . O comando *git fast-export* converte qualquer repositório para o formato do *git fast-import* format, cujo resultado você pode estudar para escrever seus exportadores, e também para converter repositórios Git para um formato legível aos humanos. Na verdade, estes comandos podem enviar repositórios de arquivos de texto por canais exclusivamente textuais. === Onde foi que tudo deu errado? === Você acabou de descobrir uma função errada em seu programa, que você sabe com certeza que estava funcionando há alguns meses atrás. Merda! Onde será que este erro começou? Se só você estivesse testando a funcionalidade que desenvolveu. Agora é tarde para reclamar. No entanto, se você estiver fazendo commit, o Git pode localizar o problema: $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d O Git verifica um estado intermediário entre as duas versões. Testa a função, e se ainda estiver errada: $ git bisect bad Senão, substitua "bad" por "good". O Git novamente o levará até um estado intermediário entre as versões definidas como good e bad, diminuindo as possibilidades. Após algumas iterações, esta busca binária o guiará até o commit onde começou o problema. Uma vez terminada sua investigação, volte ao estado original digitando: $ git bisect reset Ao invés de testar todas as mudanças manualmente, automatize a busca com: $ git bisect run my_script O Git usa o valor de retorno do comando utilizado, normalmente um único script, para decidir se uma mudança é good ou bad: o comando deve terminar retornando com o código 0 se for good, 125 se a mudança for ignorável e qualquer coisa entre 1 e 127 se for bad. Um valor negativo abortará a bissecção. Podemos fazer muito mais: a página de ajuda explica como visualizar as bissecções, examinar ou reproduzir o log da bissecção, e eliminar mudanças reconhecidamente inocentes para acelerar a busca. === Quem fez tudo dar errado? === Tal como outros sistema de controle de versões, o Git tem um comando blame (culpado): $ git blame bug.c que marca cada linha do arquivo mostrando quem o modificou por último e quando. Ao contrário de outros sistemas de controle de versões, esta operação ocorre offline, lendo apenas do disco local. === Experiência pessoal === Em um sistema de controle de versões centralizado, modificações no histórico são operações difíceis, e disponíveis apenas para administradores. Clonagem, branch e merge são impossíveis sem uma rede de comunicação. Bem como as operações básicas: navegar no histórico ou fazer commit das mudanças. Em alguns sistemas, é exigido do usuário uma conexão via rede, apenas para visualizar suas próprias modificações ou abrir um arquivo para edição. Sistemas centralizados impedem o trabalho offline, e exigem um infraestrutura de rede mais cara, especialmente quando o número de desenvolvedores aumenta. Mais importante, todas a operações são mais lentas, até certo ponto, geralmente até o ponto onde os usuários evitam comandos mais avançados até serem absolutamente necessários. Em casos extremos, esta é a regra até para a maioria dos comandos básicos. Quando os usuários devem executar comandos lento, a produtividade sofre por causa de uma interrupção no fluxo de trabalho. Já experimentei este fenômeno na pele. O Git foi o primeiro sistema de controle de versões que usei. E rapidamente cresci acostumado a ele, tomando muitas de suas características como normais. Simplesmente assumi que os outros sistemas eram semelhante: escolher um sistema de controle de versões deveria ser igual a escolher um novo editor de texto ou navegador para internet. Fiquei chocado quando, posteriormente, fui forçado a usar um sistema centralizado. Uma conexão ruim com a Internet pouco importa com o Git, mas torna o desenvolvimento insuportável quando precisa ser tão confiável quanto o disco local. Além disso, me condicionava a evitar determinados comandos devido a latência envolvida, o que me impediu, em última instância, de continuar seguindo meu fluxo de trabalho. Quando executava um comando lento, a interrupção na minha linha de pensamento causava um enorme prejuízo. Enquanto espero a comunicação com o servidor concluir, faço algo para passar o tempo, como checar email ou escrever documentação. Na hora em que retorno a tarefa original, o comando já havia finalizado há muito tempo, e perco mais tempo lembrando o que estava fazendo. Os seres humanos são ruins com trocas de contexto. Houve também um interessante efeito da tragédia dos comuns: antecipando o congestionamento da rede, os indivíduos consomem mais banda que o necessário em varias operações numa tentativa de reduzir atrasos futuros. Os esforços combinados intensificam o congestionamento, encorajando os indivíduos a consumir cada vez mais banda da próxima vez para evitar os longos atrasos. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/translate.txt���������������������������������������������������������������0000644�0001750�0001750�00000002544�12666307504�016651� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Apendice B: Traduzindo esse guia == Recomendo os seguintes passos para a tradução desse guia, de modo que meus scripts produzam a versão HTML e PDF, e que todas as traduções possam conviver em um mesmo repositório. Faça um clone do fonte, e então crie um diretório correspondente a tag IETF da idioma alvo: veja http://www.w3.org/International/articles/language-tags/Overview.en.php[o artigo da W3C sobre internacionalização]. Por exemplo, Ingles (English) é "en" e Japonês é "ja". No novo diretório, traduza os arquivos .txt a partir do subdiretorio "en". Por exemplo, para traduzir este guia para a língua http://en.wikipedia.org/wiki/Klingon_language[Klingon], você deve digitar: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" is the IETF language code for Klingon. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Translate the file. e assim por diante, para cada arquivo .txt Edite o Makefile e adicione o código do idioma na variável `TRANSLATIONS`. Você poderá então rever seu trabalho de forma incremental: $ make tlh $ firefox book-tlh/index.html Faça frequentes commits de suas alterações, e me avise quando o trabalho estiver pronto. O GitHub tem uma interface que facilita isso: faça um fork do projeto "gitmagic", faça um push de suas alterações, e então me peça para fazer um merge. ������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/intro.txt�������������������������������������������������������������������0000644�0001750�0001750�00000016011�12666307504�016001� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Introdução == Usaremos uma analogia para falar sobre controle de versões. Veja http://en.wikipedia.org/wiki/Revision_control[o verbete Sistema de Controle de Versões] para uma explicação mais formal. === Trabalhar é Divertido === Divirto-me com jogos para computador quase a minha vida toda. Em contrapartida, só comecei a usar sistemas de controle de versões quando adulto. Suspeito que não fui o único, e comparar os dois pode tornar estes conceitos mais fáceis de explicar e entender. Pense na edição de seu código, documento, ou qualquer outra coisa, como jogar um jogo. Uma vez que tenha feito muitos progressos, e gostaria de de salvá-los. Para isso, você clica em “Salvar” no seu editor preferido. Porém, isto vai sobrescrever a versão anterior. É como nos antigos jogos onde você só tinha uma espaço para salvar: você pode salvar, mas nunca mais poderá voltar a um estado salvo anteriormente. O que é um pena, pois o estado anterior era uma parte muito divertida do jogo e você gostaria de poder revisitá-lo outra hora. Ou pior, o último estado salvo é dificílimo e você terá que recomeçar. === Controle de Versões === Ao editar, você pode “Salvar como ...” num arquivo diferente, ou copiar o arquivo antes de sobrescrevê-lo se você quiser manter as versões anteriores. Pode também comprimí-los para economizar espaço. Isto é uma forma rudimentar e muito trabalhosa de controle de versões. Jogos de computador aperfeiçoaram este método, muitos deles acrescentam automaticamente a data e a hora aos estados salvos. Vamos dificultar um pouco. Digamos que são um monte de arquivos juntos, como os fontes do seu projeto, ou arquivos para um website. Agora se quiser manter suas versões anteriores, terá que arquivar todo um diretório ou vários. Manter muitas versões na mão é inconveniente e rapidamente se tornará caro. Em alguns jogos de computador, um estado salvo consiste de um diretório cheio de arquivos. Estes jogos escondem estes detalhes do jogador e lhe apresentam uma interface conveniente para gerenciar as diferentes versões neste diretório. Sistemas de controle de versões não são diferentes. Todos têm uma boa interface para gerenciar seu diretório de versões. Você pode salvar o diretório sempre que desejar, e pode rever qualquer um dos estados salvos quando quiser. Ao contrário da maioria dos jogos de computador, eles são geralmente mais espertos na economia de espaço. Normalmente, apenas uns poucos arquivos mudam de versão para versão, e com poucas diferenças. Armazenar as diferenças, ao invés de todos os arquivos, economiza espaço. === Controle distribuído === Agora imagine um jogo de computador muito difícil. Tão difícil de terminar que vários jogadores experientes pelo mundo decidem formar uma equipe e compartilhar seus estados salvos do jogo para tentar vencê-lo. Speedruns são exemplos reais: jogadores especializados em diferentes níveis do mesmo jogo colaboram na produção de resultados incríveis. Como você configura um sistema para que todos possam obter facilmente o que os outros salvarem? E salvar novos estados? Nos velhos tempos, todos os projetos utilizavam um controle de versões centralizado. Um servidor em algum lugar mantinha os jogos salvos. Ninguém mais teria todos os jogos. Cada jogador mantinha apenas alguns jogos salvos em suas maquinas. Quando algum jogador quiser avançar no jogo, ele pega o ultimo jogo salvo do servidor, joga um pouco, salva e manda de volta para o servidor para que os outros possam usar. E se um jogador quiser pegar um jogo antigo salvo por algum motivo? Talvez o jogo atual salvo esteja em um nível impossível de jogar devido alguém ter esquecido um objeto três níveis atrás, e é preciso encontrar o último jogo salvo que está em um nível que pode ser completado com sucesso. Ou talvez queira comparar dois jogos salvos para saber o quanto um jogador avançou. Podem existir vários motivos para ver uma versão antiga, mas o modus operandi é o mesmo. Têm que solicitar ao servidor centralizado a versão antiga. E quanto mais jogos salvos forem necessários, maior é o tráfego de informação. A nova geração de sistemas de controle de versões, dentre eles o Git, é conhecida como sistemas distribuídos, e pode ser pensada como uma generalização dos sistemas centralizados. Quando os jogadores baixam do servidor principal, eles recebem todos os jogos salvos, e não apenas o mais recente. É como se estivessem espelhando o servidor principal. A primeira operação de clonagem pode ser bem demorada, especialmente se há um longo histórico, mas é compensada no longo prazo. Um benefício imediato é que, se por qualquer razão desejar uma antiga versão, o trafego de informação com o servidor é desnecessário. === Uma superstição === Um equívoco popular é que sistemas distribuídos estão mal adaptados a projetos que exijam um repositório central oficial. Nada poderia estar mais longe da verdade. Fotografar alguém não irá roubar a sua alma. Igualmente, clonar o repositório master não diminui sua importância. Uma boa comparação inicial é: qualquer coisa que um sistema centralizado de controle de versões faz, um sistema distribuído de controle de versões bem concebido pode fazer melhor. Recursos de rede são simplesmente mais onerosos que recursos locais. Embora vejamos mais adiante que existem inconvenientes numa abordagem distribuída, é menos provável que alguém faça comparações errôneas com esta regra de ouro. Um pequeno projeto pode precisar de apenas uma fração dos recursos oferecidos pelo sistema, mas utilizar um sistema que não permite expansão é como utilizar os algarismos romanos quando calculamos com números pequenos. E mais, seu projeto pode crescer além da suas expectativas. Usando Git, desde o inicio é como ter um canivete suíço, embora o use na maioria das vezes para abrir garrafas. No dia que necessitar, desesperadamente, de uma chave de fenda você agradecerá por ter mais do que um abridor de garrafas. === Conflitos de mesclagem (Merge) === Neste tópico, nossa analogia com jogos de computador torna-se ruim. Em vez disso, vamos considerar novamente a edição de um documento. Suponha que Alice insira uma linha no início do arquivo, e Bob uma no final. Ambos enviam suas alterações. A maioria dos sistemas irá de maneira automática e reativa deduzir o plano de ação: aceitando e mesclando as mudanças, assim as alterações de Alice e Bob serão aplicadas. Agora suponha que ambos, Alice e Bob, façam alterações distintas na mesma linha. Tornando impossível resolver o conflito sem intervenção humana. A segunda pessoa, entre Alice e Bob, que enviar suas alterações será informado do conflito, e escolherá se aplica sua alteração sobre a do outro, ou revisa a linha para manter ambas as alterações. Situações muito mais complexas podem surgir. Sistemas de controle de versões são capazes de resolver os casos mais simples, e deixar os casos mais difíceis para nós resolvermos. Normalmente seu comportamento é configurável. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/pt_br/clone.txt�������������������������������������������������������������������0000644�0001750�0001750�00000027163�12666307504�015760� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Um pouco de clonagem == Em sistemas de controle de versões mais antigos, checkout é a operação padrão para se obter arquivos. Obtendo assim os arquivos do ponto de salvamento informado. No Git e em outros sistemas distribuídos de controle de versões, clonagem é a operação padrão. Para obter os arquivos, criamos um clone do repositório inteiro. Em outras palavras, você praticamente faz um espelhamento do servidor central. Tudo o que se pode fazer no repositório principal, você pode fazer no seu repositório local. === Sincronizando Computadores === Eu posso aguentar fazer tarball1 ou usar o *rsync* para backup e sincronizações básicas. Mas, às vezes edito no meu laptop, outras no meu desktop, e os dois podem não ter conversado entre si nesse período. Inicialize um repositório Git e faça um commit seus arquivos em uma das máquinas. Então faça na outra máquina: $ git clone other.computer:/path/to/files para criar uma segunda cópia dos seus arquivos e do repositório Git. A partir de agora, use: $ git commit -a $ git pull other.computer:/path/to/files HEAD o que deixará os arquivos da máquina em que você está trabalhando, no mesmo estado que estão no outro computador. Se você recentemente fez alguma alteração conflitante no mesmo arquivo, o Git lhe informará e você poderá fazer um novo commit e então escolher o que fazer para resolvê-lo. === Controle clássico de código === Inicialize um repositório Git para seus arquivos: $ git init $ git add . $ git commit -m "Initial commit" No servidor principal, inicialize um 'repositório vazio' do Git em algum diretório: $ mkdir proj.git $ cd proj.git $ git --bare init $ touch proj.git/git-daemon-export-ok E inicie o daemon Git se necessário: $ git daemon --detach # it may already be running Algumas hospedagens públicas, siga as instruções para configuração de um repositório git vazio. Na maioria das vezes, isso é feito através do preenchimento de um formulário no site deles. Envie ('Push') seu projeto para o servidor principal com: $ git push central.server/path/to/proj.git HEAD Para verificar os fontes, um desenvolvedor pode digitar: $ git clone central.server/path/to/proj.git Após realizar as alterações, o desenvolvedor pode salvar as alterações localmente com: $ git commit -a Para atualizar para a ultima versão: $ git pull Qualquer conflito de merge deve ser resolvido e então feito o commit: $ git commit -a Para verificar as mudanças locais no repositório central: $ git push Se o servidor principal possui novas alterações devido a atividades de outros desenvolvedores, o push irá falhar, e o desenvolvedor deverá fazer o pull da ultima versão, resolver qualquer conflito de merge, e então tentar novamente. Os desenvolvedores devem ter acesso a SSH para utilizar os comandos de push e pull acima. Entretanto qualquer pessoa pode examinar os arquivos-fonte, digitando: $ git clone git://central.server/path/to/proj.git O protocolo nativo do Git é semelhante ao HTTP, não existe nenhum tipo de autenticação, de modo que qualquer um pode baixar o projeto. Da mesma maneira, o push, por default, é proibido pelo protocolo Git. === Codigo-fonte secreto === Para um projeto que não seja de código aberto, omita o comando touch e garanta que você nunca irá criar um arquivo com o nome `git-daemon-export-ok`. O repositório não poderá ser baixado via protocolo git; somente aqueles com acesso SSH poderão visualiza-lo. Se todos os seus repósitórios forem fechados, não é necessária a execução do daemon do git, pois todas as comunicações irão ocorrer por meio do SSH. === Repositorios Vazios === Um repositório é chamado de vazio (bare) porque ele não possui um diretório de trabalho; ele contém somente arquivos que normalmente estão escondidos no subdiretório `.git`. Em outras palavras, ele mantém a história de um projeto, e nunca guarda uma “fotografia” de alguma versão. Um repositório vazio tem um papel semelhante aquele do servidor principal nos sistemas de controle de versão centralizados: o diretório principal (home) de seu projeto. Os desenvolvedores clonam seu projeto a partir dele, e fazem o push da ultima versão oficial para ele. Geralmente, ele reside em um servidor que somente dissemina os dados. O desenvolvimento ocorre nos clones, de modo que o repositório principal (home) pode funcionar sem um diretório de trabalho. Muitos comandos git falham em repositórios vazios a não ser que a variável de ambiente `GIT_DIR` seja configurada para o path do repositório, ou a opção `--bare` seja fornecida. === Push versus Pull === Por que nós falamos do comando push, ao invés de basearmos no familar comando pull? Em primeiro lugar, porque o pulling falha em repositórios vazios: ao contrário você deve fazer um fetch, um comando que será discutido mais adiante. Mas mesmo que utilizamos um repositório normal em um servidor centralizado, fazer o pull para ele ainda será problemático. Primeiro, teremos que fazer o login no servidor, e então entrar com o comando pull com o endereço de rede da máquina que estamos fazendo o pull. Os firewall podem interferir, e o que dizer quando não temos acesso a uma janela de shell do servidor? Entretanto, além desse caso, desencorajamos o push em um repositório , por causa da confusão que pode ocorrer quando o destino possui um diretório de trabalho. Em resumo, enquanto estiver aprendendo a utilizar o git, somente faça push quando o alvo for um repositório vazio, caso contrário utilize o pull. === Fazendo um Fork do Projeto === Chateado com a rumo que o seu projeto está tomando? Acha que pode fazer um trabalho melhor? Então no seu servidor: $ git clone git://main.server/path/to/files Em seguida avise a todos sobre seu fork do projeto no seu servidor. A qualquer hora, você poderá mesclar (merge) suas mudanças do projeto original no mesmo com: $ git pull === Backup Supremos === Gostaria de ter vários arquivos geográficamente dispersos, redundantes e anti-falsificações? Se seu projeto tem muitos desenvolvedores, não faça nada! Cada clonagem do seu código é um backup efetivo. E não apenas uma cópia do estado atual, e sim o histórico completo do seu projeto. Graças ao hash criptográfico, se alguma clonagem for corrompida, ela será identificada assim que tentar se comunicar com as outras. Se seu projeto não é tão popular, encontre quantos servidores puder para hospedar seus clones. Um paranóico verdadeiro sempre anotará os últimos 20 byte do hash SHA1 do cabeçalho (HEAD) em algum lugar seguro. Tem que ser seguro, e não privado. Por exemplo, publicá-lo em um jornal funciona bem, pois é muito difícil para um atacante alterar todas as cópias de um jornal. === Multitarefa na velocidade da luz === Digamos que você queira trabalhar em diversas funções em paralelo. Então, faça um commit do seu projeto executando: $ git clone . /some/new/directory Graças aos http://en.wikipedia.org/wiki/Hard_link[hardlinking], os clones locais necessitam de menos tempo e espaço do que os backups comuns. Agora você pode trabalhar em duas funções independentes de forma simultânea. Por exemplo, pode editar um clone enquanto o outro está sendo compilado. A qualquer momento, você pode fazer um commit e pegar (pull) as alterações de outro clone: $ git pull /the/other/clone HEAD === Controle de Versões de Guerrilha === Você está trabalhando em um projeto que utiliza outro sistema de controle de versões, e sente saudade do Git? Inicialize um repositório Git no seu diretório de trabalho: $ git init $ git add . $ git commit -m "Initial commit" Faça um clone dele: $ git clone . /some/new/directory Agora vá para o novo diretório e trabalhe nele, não no anterior, usando Git para felicidade geral da nação. De vez em quando você desejará sincronizar com os outros, neste caso, vá para o diretório original, sincronize usando o outro sistema de controle de versões, e então digite: $ git add . $ git commit -m "Sync with everyone else" Depois vá para o novo diretório e execute: $ git commit -a -m "Description of my changes" $ git pull O procedimento para enviar suas alterações para os outros depende do outro sistema de controle de versões. O novo diretório contém os arquivos com as suas alterações. Execute qualquer comando do outro sistema de controle de versões necessário para enviá-las para o repositório central. O subversion, é talvez o melhor sistema de controle de versões centralizado, é utilizado em muitos projetos. O comando *git svn* automatiza tudo isso para repositórios Subversion, e também pode ser utilizado para http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[exportar um repositório Git para um repositório Subversion]. === Mercurial === Mercurial é um sistema de controle de versões semelhante, e que pode trabalhar perfeitamente junto com o Git. Com o plugin `hg-git`, um usuário do Mercurial pode realizar push e pulls de um repositorio Git. Para obter o `hg-git` plugin com o Git: $ git clone git://github.com/schacon/hg-git.git ou no Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ Infelizmente, não conheço de um plugin semelhante para o Git. Por essa razão, aconselho o Git no lugar do Mercurial para o repositório principal, mesmo que você prefira o Mercurial. Com o Mercurial, geralmente um voluntário mantém um repositorio Git paralelo para acomodar os usuários Git, e graças ao plugin `hg-git`, um projeto Git automaticamente acomoda os usuários Mercurial. Embora o plugin converta um repositorio Mercurial em um repositório Git fazendo o push para um repositório vazio, esse serviço é mais fácil de fazer com o script `hg-fast-export.sh` disponível em: $ git clone git://repo.or.cz/fast-export.git Para fazer a conversão, em um diretório vazio, execute: $ git init $ hg-fast-export.sh -r /hg/repo após adicionar o script ao seu `$PATH`. === Bazaar === Vamos mencionar o Bazaar rapidamente por que é o sistema de controle de versões distribuído mais utilizado, depois do Git e do Mercurial. Bazaar tem a vantagem de retrospectiva, já que é relativamente recente; seus projetistas puderam aprender com os erros dos projetos anteriores, e evitar as principais falhas. Além disso, seus desenvolvedores estão preocupados com a portabilidade e interoperação com outros sistemas de controle de versão. O plugin `bzr-git` permite que os usuários do Bazaar trabalhem com os repositorios Git de alguma forma. O programa `tailor` converte um repositório Bazaar em repositórios Git, e pode fazer isso de forma incremental, enquanto que `bzr-fast-export` é adequada para realizar conversões uma única vez. === Por que uso o Git? === Originalmente, escolhi o Git porque escutei que poderia gerenciar o inimaginável e ingerenciável código fonte do kernel do Linux. E nunca senti necessidade de mudar. O Git tem me servido admiravelmente bem, e já estou acostumado com suas falhas. Como utilizo na maior parte do tempo o Linux, os problemas nas outras plataformas não são importantes. Também, prefiro programas em C e scripts bash a executáveis tais como scripts Python: existem poucas dependências, e sou viciado em tempos de execução muito rápidos. Já pensei também como o Git poderia ser melhorado, criando minha própria ferramenta Git, mas somente como um exercício académico. Assim que completei meu projeto, continuei com o git, pois os ganhos seriam mínimos para justificar a utilização de um sistema diferente. Naturalmente, você pode e precisa pensar diferente, e pode se sentir melhor com outro sistema. No entanto, você não pode estar muito errado com o Git. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/en/�������������������������������������������������������������������������������0000755�0001750�0001750�00000000000�12666307504�013402� 5����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/en/drawbacks.txt������������������������������������������������������������������0000644�0001750�0001750�00000015353�12666307504�016113� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Appendix A: Git Shortcomings == There are some Git issues I've swept under the carpet. Some can be handled easily with scripts and hooks, some require reorganizing or redefining the project, and for the few remaining annoyances, one will just have to wait. Or better yet, pitch in and help! === SHA1 Weaknesses === As time passes, cryptographers discover more and more SHA1 weaknesses. Already, finding hash collisions is feasible for well-funded organizations. Within years, perhaps even a typical PC will have enough computing power to silently corrupt a Git repository. Hopefully Git will migrate to a better hash function before further research destroys SHA1. === Microsoft Windows === Git on Microsoft Windows can be cumbersome: - http://cygwin.com/[Cygwin], a Linux-like environment for Windows, contains http://cygwin.com/packages/git/[a Windows port of Git]. - http://code.google.com/p/msysgit/[Git on MSys] is an alternative requiring minimal runtime support, though a few of the commands need some work. === Unrelated Files === If your project is very large and contains many unrelated files that are constantly being changed, Git may be disadvantaged more than other systems because single files are not tracked. Git tracks changes to the whole project, which is usually beneficial. A solution is to break up your project into pieces, each consisting of related files. Use *git submodule* if you still want to keep everything in a single repository. === Who's Editing What? === Some version control systems force you to explicitly mark a file in some way before editing. While this is especially annoying when this involves talking to a central server, it does have two benefits: 1. Diffs are quick because only the marked files need be examined. 2. One can discover who else is working on the file by asking the central server who has marked it for editing. With appropriate scripting, you can achieve the same with Git. This requires cooperation from the programmer, who should execute particular scripts when editing a file. === File History === Since Git records project-wide changes, reconstructing the history of a single file requires more work than in version control systems that track individual files. The penalty is typically slight, and well worth having as other operations are incredibly efficient. For example, `git checkout` is faster than `cp -a`, and project-wide deltas compress better than collections of file-based deltas. === Initial Clone === Creating a clone is more expensive than checking out code in other version control systems when there is a lengthy history. 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. === Volatile Projects === Git was written to be fast with respect to the size of the changes. Humans make small edits from version to version. A one-liner bugfix here, a new feature there, emended comments, and so forth. But if your files are radically different in successive revisions, then on each commit, your history necessarily grows by the size of your whole project. There is nothing any version control system can do about this, but standard Git users will suffer more since normally histories are cloned. The reasons why the changes are so great should be examined. Perhaps file formats should be changed. Minor edits should only cause minor changes to at most a few files. Or perhaps a database or backup/archival solution is what is actually being sought, not a version control system. For example, version control may be ill-suited for managing photos periodically taken from a webcam. If the files really must be constantly morphing and they really must be versioned, a possibility is to use Git in a centralized fashion. One can create shallow clones, which checks out little or no history of the project. Of course, many Git tools will be unavailable, and fixes must be submitted as patches. This is probably fine as it's unclear why anyone would want the history of wildly unstable files. Another example is a project depending on firmware, which takes the form of a huge binary file. The history of the firmware is uninteresting to users, and updates compress poorly, so firmware revisions would unnecessarily blow up the size of the repository. In this case, the source code should be stored in a Git repository, and the binary file should be kept separately. To make life easier, one could distribute a script that uses Git to clone the code, and rsync or a Git shallow clone for the firmware. === Global Counter === Some centralized version control systems maintain a positive integer that increases when a new commit is accepted. Git refers to changes by their hash, which is better in many circumstances. But some people like having this integer around. Luckily, it's easy to write scripts so that with every update, the central Git repository increments an integer, perhaps in a tag, and associates it with the hash of the latest commit. Every clone could maintain such a counter, but this would probably be useless, since only the central repository and its counter matters to everyone. === Empty Subdirectories === Empty subdirectories cannot be tracked. Create dummy files to work around this problem. The current implementation of Git, rather than its design, is to blame for this drawback. With luck, once Git gains more traction, more users will clamour for this feature and it will be implemented. === Initial Commit === A stereotypical computer scientist counts from 0, rather than 1. Unfortunately, with respect to commits, git does not adhere to this convention. Many commands are unfriendly before the initial commit. Additionally, some corner cases must be handled specially, such as rebasing a branch with a different initial commit. Git would benefit from defining the zero commit: as soon as a repository is constructed, HEAD would be set to the string consisting of 20 zero bytes. This special commit represents an empty tree, with no parent, at some time predating all Git repositories. Then running git log, for example, would inform the user that no commits have been made yet, instead of exiting with a fatal error. Similarly for other tools. Every initial commit is implicitly a descendant of this zero commit. However there are some problem cases unfortunately. If several branches with different initial commits are merged together, then rebasing the result requires substantial manual intervention. === Interface Quirks === For commits A and B, the meaning of the expressions "A..B" and "A...B" depends on whether the command expects two endpoints or a range. See *git help diff* and *git help rev-parse*. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/en/branch.txt���������������������������������������������������������������������0000644�0001750�0001750�00000026165�12666307504�015412� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Branch Wizardry == Instant branching and merging are the most lethal of Git's killer features. *Problem*: External factors inevitably necessitate context switching. A severe bug manifests in the released version without warning. The deadline for a certain feature is moved closer. A developer whose help you need for a key section of the project is about to leave. In all cases, you must abruptly drop what you are doing and focus on a completely different task. Interrupting your train of thought can be detrimental to your productivity, and the more cumbersome it is to switch contexts, the greater the loss. With centralized version control we must download a fresh working copy from the central server. Distributed systems fare better, as we can clone the desired version locally. But cloning still entails copying the whole working directory as well as the entire history up to the given point. Even though Git reduces the cost of this with file sharing and hard links, the project files themselves must be recreated in their entirety in the new working directory. *Solution*: Git has a better tool for these situations that is much faster and more space-efficient than cloning: *git branch*. With this magic word, the files in your directory suddenly shapeshift from one version to another. This transformation can do more than merely go back or forward in history. Your files can morph from the last release to the experimental version to the current development version to your friend's version and so on. === The Boss Key === Ever played one of those games where at the push of a button (``the boss key''), the screen would instantly display a spreadsheet or something? So if the boss walked in the office while you were playing the game you could quickly hide it away? In some directory: $ echo "I'm smarter than my boss" > myfile.txt $ git init $ git add . $ git commit -m "Initial commit" We have created a Git repository that tracks one text file containing a certain message. Now type: $ git checkout -b boss # nothing seems to change after this $ echo "My boss is smarter than me" > myfile.txt $ git commit -a -m "Another commit" It looks like we've just overwritten our file and committed it. But it's an illusion. Type: $ git checkout master # switch to original version of the file and hey presto! The text file is restored. And if the boss decides to snoop around this directory, type: $ git checkout boss # switch to version suitable for boss' eyes You can switch between the two versions of the file as much as you like, and commit to each independently. === Dirty Work === [[branch]] Say you're working on some feature, and for some reason, you need to go back three versions and temporarily put in a few print statements to see how something works. Then: $ git commit -a $ git checkout HEAD~3 Now you can add ugly temporary code all over the place. You can even commit these changes. When you're done, $ git checkout master to return to your original work. Observe that any uncommitted changes are carried over. What if you wanted to save the temporary changes after all? Easy: $ git checkout -b dirty and commit before switching back to the master branch. Whenever you want to return to the dirty changes, simply type: $ git checkout dirty We touched upon this command in an earlier chapter, when discussing loading old states. At last we can tell the whole story: the files change to the requested state, but we must leave the master branch. Any commits made from now on take your files down a different road, which can be named later. In other words, after checking out an old state, Git automatically puts you in a new, unnamed branch, which can be named and saved with *git checkout -b*. === Quick Fixes === You're in the middle of something when you are told to drop everything and fix a newly discovered bug in commit `1b6d...`: $ git commit -a $ git checkout -b fixes 1b6d Then once you've fixed the bug: $ git commit -a -m "Bug fixed" $ git checkout master and resume work on your original task. You can even 'merge' in the freshly baked bugfix: $ git merge fixes === Merging === With some version control systems, creating branches is easy but merging them back together is tough. With Git, merging is so trivial that you might be unaware of it happening. We actually encountered merging long ago. The *pull* command in fact 'fetches' commits and then merges them into your current branch. If you have no local changes, then the merge is a 'fast forward', a degenerate case akin to fetching the latest version in a centralized version control system. But if you do have local changes, Git will automatically merge, and report any conflicts. Ordinarily, a commit has exactly one 'parent commit', namely, the previous commit. Merging branches together produces a commit with at least two parents. This begs the question: what commit does `HEAD~10` really refer to? A commit could have multiple parents, so which one do we follow? It turns out this notation chooses the first parent every time. This is desirable because the current branch becomes the first parent during a merge; frequently you're only concerned with the changes you made in the current branch, as opposed to changes merged in from other branches. You can refer to a specific parent with a caret. For example, to show the logs from the second parent: $ git log HEAD^2 You may omit the number for the first parent. For example, to show the differences with the first parent: $ git diff HEAD^ You can combine this notation with other types. For example: $ git checkout 1b6d^^2~10 -b ancient starts a new branch ``ancient'' representing the state 10 commits back from the second parent of the first parent of the commit starting with 1b6d. === Uninterrupted Workflow === Often in hardware projects, the second step of a plan must await the completion of the first step. A car undergoing repairs might sit idly in a garage until a particular part arrives from the factory. A prototype might wait for a chip to be fabricated before construction can continue. Software projects can be similar. The second part of a new feature may have to wait until the first part has been released and tested. Some projects require your code to be reviewed before accepting it, so you might wait until the first part is approved before starting the second part. Thanks to painless branching and merging, we can bend the rules and work on Part II before Part I is officially ready. Suppose you have committed Part I and sent it for review. Let's say you're in the `master` branch. Then branch off: $ git checkout -b part2 Next, work on Part II, committing your changes along the way. To err is human, and often you'll want to go back and fix something in Part I. If you're lucky, or very good, you can skip these lines. $ git checkout master # Go back to Part I. $ fix_problem $ git commit -a # Commit the fixes. $ git checkout part2 # Go back to Part II. $ git merge master # Merge in those fixes. Eventually, Part I is approved: $ git checkout master # Go back to Part I. $ submit files # Release to the world! $ git merge part2 # Merge in Part II. $ git branch -d part2 # Delete "part2" branch. Now you're in the `master` branch again, with Part II in the working directory. It's easy to extend this trick for any number of parts. It's also easy to branch off retroactively: suppose you belatedly realize you should have created a branch 7 commits ago. Then type: $ git branch -m master part2 # Rename "master" branch to "part2". $ git branch master HEAD~7 # Create new "master", 7 commits upstream. The `master` branch now contains just Part I, and the `part2` branch contains the rest. We are in the latter branch; we created `master` without switching to it, because we want to continue work on `part2`. This is unusual. Until now, we've been switching to branches immediately after creation, as in: $ git checkout HEAD~7 -b master # Create a branch, and switch to it. === Reorganizing a Medley === Perhaps you like to work on all aspects of a project in the same branch. You want to keep works-in-progress to yourself and want others to see your commits only when they have been neatly organized. Start a couple of branches: $ git branch sanitized # Create a branch for sanitized commits. $ git checkout -b medley # Create and switch to a branch to work in. Next, work on anything: fix bugs, add features, add temporary code, and so forth, committing often along the way. Then: $ git checkout sanitized $ git cherry-pick medley^^ applies the grandparent of the head commit of the ``medley'' branch to the ``sanitized'' branch. With appropriate cherry-picks you can construct a branch that contains only permanent code, and has related commits grouped together. === Managing Branches === List all branches by typing: $ git branch By default, you start in a branch named ``master''. Some advocate leaving the ``master'' branch untouched and creating new branches for your own edits. The *-d* and *-m* options allow you to delete and move (rename) branches. See *git help branch*. The ``master'' branch is a useful custom. Others may assume that your repository has a branch with this name, and that it contains the official version of your project. Although you can rename or obliterate the ``master'' branch, you might as well respect this convention. === Temporary Branches === After a while you may realize you are creating short-lived branches frequently for similar reasons: every other branch merely serves to save the current state so you can briefly hop back to an older state to fix a high-priority bug or something. It's analogous to changing the TV channel temporarily to see what else is on. But instead of pushing a couple of buttons, you have to create, check out, merge, and delete temporary branches. Luckily, Git has a shortcut that is as convenient as a TV remote control: $ git stash This saves the current state in a temporary location (a 'stash') and restores the previous state. Your working directory appears exactly as it was before you started editing, and you can fix bugs, pull in upstream changes, and so on. When you want to go back to the stashed state, type: $ git stash apply # You may need to resolve some conflicts. You can have multiple stashes, and manipulate them in various ways. See *git help stash*. As you may have guessed, Git maintains branches behind the scenes to perform this magic trick. === Work How You Want === You might wonder if branches are worth the bother. After all, clones are almost as fast, and you can switch between them with *cd* instead of esoteric Git commands. Consider web browsers. Why support multiple tabs as well as multiple windows? Because allowing both accommodates a wide variety of styles. Some users like to keep only one browser window open, and use tabs for multiple webpages. Others might insist on the other extreme: multiple windows with no tabs anywhere. Others still prefer something in between. Branching is like tabs for your working directory, and cloning is like opening a new browser window. These operations are fast and local, so why not experiment to find the combination that best suits you? Git lets you work exactly how you want. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/en/secrets.txt��������������������������������������������������������������������0000644�0001750�0001750�00000026511�12666307504�015620� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Secrets Revealed == We take a peek under the hood and explain how Git performs its miracles. I will skimp over details. For in-depth descriptions refer to http://schacon.github.com/git/user-manual.html[the user manual]. === Invisibility === How can Git be so unobtrusive? Aside from occasional commits and merges, you can work as if you were unaware that version control exists. That is, until you need it, and that's when you're glad Git was watching over you the whole time. Other version control systems force you to constantly struggle with red tape and bureaucracy. Permissions of files may be read-only unless you explicitly tell a central server which files you intend to edit. The most basic commands may slow to a crawl as the number of users increases. Work grinds to a halt when the network or the central server goes down. In contrast, Git simply keeps the history of your project in the `.git` directory in your working directory. This is your own copy of the history, so you can stay offline until you want to communicate with others. You have total control over the fate of your files because Git can easily recreate a saved state from `.git` at any time. === Integrity === Most people associate cryptography with keeping information secret, but another equally important goal is keeping information safe. Proper use of cryptographic hash functions can prevent accidental or malicious data corruption. A SHA1 hash can be thought of as a unique 160-bit ID number for every string of bytes you'll encounter in your life. Actually more than that: every string of bytes that any human will ever use over many lifetimes. As a SHA1 hash is itself a string of bytes, we can hash strings of bytes containing other hashes. This simple observation is surprisingly useful: look up 'hash chains'. We'll later see how Git uses it to efficiently guarantee data integrity. Briefly, Git keeps your data in the `.git/objects` subdirectory, where instead of normal filenames, you'll find only IDs. By using IDs as filenames, as well as a few lockfiles and timestamping tricks, Git transforms any humble filesystem into an efficient and robust database. === Intelligence === How does Git know you renamed a file, even though you never mentioned the fact explicitly? Sure, you may have run *git mv*, but that is exactly the same as a *git rm* followed by a *git add*. Git heuristically ferrets out renames and copies between successive versions. In fact, it can detect chunks of code being moved or copied around between files! Though it cannot cover all cases, it does a decent job, and this feature is always improving. If it fails to work for you, try options enabling more expensive copy detection, and consider upgrading. === Indexing === For every tracked file, Git records information such as its size, creation time and last modification time in a file known as the 'index'. To determine whether a file has changed, Git compares its current stats with those cached in the index. If they match, then Git can skip reading the file again. Since stat calls are considerably faster than file reads, if you only edit a few files, Git can update its state in almost no time. We stated earlier that the index is a staging area. Why is a bunch of file stats a staging area? Because the add command puts files into Git's database and updates these stats, while the commit command, without options, creates a commit based only on these stats and the files already in the database. === Git's Origins === This http://lkml.org/lkml/2005/4/6/121[Linux Kernel Mailing List post] describes the chain of events that led to Git. The entire thread is a fascinating archaeological site for Git historians. === The Object Database === Every version of your data is kept in the 'object database', which lives in the subdirectory `.git/objects`; the other residents of `.git/` hold lesser data: the index, branch names, tags, configuration options, logs, the current location of the head commit, and so on. The object database is elementary yet elegant, and the source of Git's power. Each file within `.git/objects` is an 'object'. There are 3 kinds of objects that concern us: 'blob' objects, 'tree' objects, and 'commit' objects. === Blobs === First, a magic trick. Pick a filename, any filename. In an empty directory: $ echo sweet > YOUR_FILENAME $ git init $ git add . $ find .git/objects -type f You'll see +.git/objects/aa/823728ea7d592acc69b36875a482cdf3fd5c8d+. How do I know this without knowing the filename? It's because the SHA1 hash of: "blob" SP "6" NUL "sweet" LF is aa823728ea7d592acc69b36875a482cdf3fd5c8d, where SP is a space, NUL is a zero byte and LF is a linefeed. You can verify this by typing: $ printf "blob 6\000sweet\n" | sha1sum Git is 'content-addressable': files are not stored according to their filename, but rather by the hash of the data they contain, in a file we call a 'blob object'. We can think of the hash as a unique ID for a file's contents, so in a sense we are addressing files by their content. The initial `blob 6` is merely a header consisting of the object type and its length in bytes; it simplifies internal bookkeeping. Thus I could easily predict what you would see. The file's name is irrelevant: only the data inside is used to construct the blob object. You may be wondering what happens to identical files. Try adding copies of your file, with any filenames whatsoever. The contents of +.git/objects+ stay the same no matter how many you add. Git only stores the data once. By the way, the files within +.git/objects+ are compressed with zlib so you should not stare at them directly. Filter them through http://www.zlib.net/zpipe.c[zpipe -d], or type: $ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d which pretty-prints the given object. === Trees === But where are the filenames? They must be stored somewhere at some stage. Git gets around to the filenames during a commit: $ git commit # Type some message. $ find .git/objects -type f You should now see 3 objects. This time I cannot tell you what the 2 new files are, as it partly depends on the filename you picked. We'll proceed assuming you chose ``rose''. If you didn't, you can rewrite history to make it look like you did: $ git filter-branch --tree-filter 'mv YOUR_FILENAME rose' $ find .git/objects -type f Now you should see the file +.git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9+, because this is the SHA1 hash of its contents: "tree" SP "32" NUL "100644 rose" NUL 0xaa823728ea7d592acc69b36875a482cdf3fd5c8d Check this file does indeed contain the above by typing: $ echo 05b217bb859794d08bb9e4f7f04cbda4b207fbe9 | git cat-file --batch With zpipe, it's easy to verify the hash: $ zpipe -d < .git/objects/05/b217bb859794d08bb9e4f7f04cbda4b207fbe9 | sha1sum Hash verification is trickier via cat-file because its output contains more than the raw uncompressed object file. This file is a 'tree' object: a list of tuples consisting of a file type, a filename, and a hash. In our example, the file type is 100644, which means `rose` is a normal file, and the hash is the blob object that contains the contents of `rose'. Other possible file types are executables, symlinks or directories. In the last case, the hash points to a tree object. If you ran filter-branch, you'll have old objects you no longer need. Although they will be jettisoned automatically once the grace period expires, we'll delete them now to make our toy example easier to follow: $ rm -r .git/refs/original $ git reflog expire --expire=now --all $ git prune For real projects you should typically avoid commands like this, as you are destroying backups. If you want a clean repository, it is usually best to make a fresh clone. Also, take care when directly manipulating +.git+: what if a Git command is running at the same time, or a sudden power outage occurs? In general, refs should be deleted with *git update-ref -d*, though usually it's safe to remove +refs/original+ by hand. === Commits === We've explained 2 of the 3 objects. The third is a 'commit' object. Its contents depend on the commit message as well as the date and time it was created. To match what we have here, we'll have to tweak it a little: $ git commit --amend -m Shakespeare # Change the commit message. $ git filter-branch --env-filter 'export GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800" GIT_AUTHOR_NAME="Alice" GIT_AUTHOR_EMAIL="alice@example.com" GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800" GIT_COMMITTER_NAME="Bob" GIT_COMMITTER_EMAIL="bob@example.com"' # Rig timestamps and authors. $ find .git/objects -type f You should now see +.git/objects/49/993fe130c4b3bf24857a15d7969c396b7bc187+ which is the SHA1 hash of its contents: "commit 158" NUL "tree 05b217bb859794d08bb9e4f7f04cbda4b207fbe9" LF "author Alice <alice@example.com> 1234567890 -0800" LF "committer Bob <bob@example.com> 1234567890 -0800" LF LF "Shakespeare" LF As before, you can run zpipe or cat-file to see for yourself. This is the first commit, so there are no parent commits, but later commits will always contain at least one line identifying a parent commit. === Indistinguishable From Magic === Git's secrets seem too simple. It looks like you could mix together a few shell scripts and add a dash of C code to cook it up in a matter of hours: a melange of basic filesystem operations and SHA1 hashing, garnished with lock files and fsyncs for robustness. In fact, this accurately describes the earliest versions of Git. Nonetheless, apart from ingenious packing tricks to save space, and ingenious indexing tricks to save time, we now know how Git deftly changes a filesystem into a database perfect for version control. For example, if any file within the object database is corrupted by a disk error, then its hash will no longer match, alerting us to the problem. By hashing hashes of other objects, we maintain integrity at all levels. Commits are atomic, that is, a commit can never only partially record changes: we can only compute the hash of a commit and store it in the database after we already have stored all relevant trees, blobs and parent commits. The object database is immune to unexpected interruptions such as power outages. We defeat even the most devious adversaries. Suppose somebody attempts to stealthily modify the contents of a file in an ancient version of a project. To keep the object database looking healthy, they must also change the hash of the corresponding blob object since it's now a different string of bytes. This means they'll have to change the hash of any tree object referencing the file, and in turn change the hash of all commit objects involving such a tree, in addition to the hashes of all the descendants of these commits. This implies the hash of the official head differs to that of the bad repository. By following the trail of mismatching hashes we can pinpoint the mutilated file, as well as the commit where it was first corrupted. In short, so long as the 20 bytes representing the last commit are safe, it's impossible to tamper with a Git repository. What about Git's famous features? Branching? Merging? Tags? Mere details. The current head is kept in the file +.git/HEAD+, which contains a hash of a commit object. The hash gets updated during a commit as well as many other commands. Branches are almost the same: they are files in +.git/refs/heads+. Tags too: they live in +.git/refs/tags+ but they are updated by a different set of commands. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/en/multiplayer.txt����������������������������������������������������������������0000644�0001750�0001750�00000020772�12666307504�016522� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Multiplayer Git == Initially I used Git on a private project where I was the sole developer. Amongst the commands related to Git's distributed nature, I needed only *pull* and *clone* so could I keep the same project in different places. Later I wanted to publish my code with Git, and include changes from contributors. I had to learn how to manage projects with multiple developers from all over the world. Fortunately, this is Git's forte, and arguably its raison d'être. === Who Am I? === Every commit has an author name and email, which is shown by *git log*. By default, Git uses system settings to populate these fields. To set them explicitly, type: $ git config --global user.name "John Doe" $ git config --global user.email johndoe@example.com Omit the global flag to set these options only for the current repository. === Git Over SSH, HTTP === Suppose you have SSH access to a web server, but Git is not installed. Though less efficient than its native protocol, Git can communicate over HTTP. Download, compile and install Git in your account, and create a repository in your web directory: $ GIT_DIR=proj.git git init $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update For older versions of Git, the copy command fails and you should run: $ chmod a+x hooks/post-update Now you can publish your latest edits via SSH from any clone: $ git push web.server:/path/to/proj.git master and anybody can get your project with: $ git clone http://web.server/proj.git === Git Over Anything === Want to synchronize repositories without servers, or even a network connection? Need to improvise during an emergency? We've seen <<makinghistory, *git fast-export* and *git fast-import* can convert repositories to a single file and back>>. We could shuttle such files back and forth to transport git repositories over any medium, but a more efficient tool is *git bundle*. The sender creates a 'bundle': $ git bundle create somefile HEAD then transports the bundle, +somefile+, to the other party somehow: email, thumb drive, an *xxd* printout and an OCR scanner, reading bits over the phone, smoke signals, etc. The receiver retrieves commits from the bundle by typing: $ git pull somefile The receiver can even do this from an empty repository. Despite its size, +somefile+ contains the entire original git repository. In larger projects, eliminate waste by bundling only changes the other repository lacks. For example, suppose the commit ``1b6d...'' is the most recent commit shared by both parties: $ git bundle create somefile HEAD ^1b6d If done frequently, one could easily forget which commit was last sent. The help page suggests using tags to solve this. Namely, after you send a bundle, type: $ git tag -f lastbundle HEAD and create new refresher bundles with: $ git bundle create newbundle HEAD ^lastbundle === Patches: The Global Currency === Patches are text representations of your changes that can be easily understood by computers and humans alike. This gives them universal appeal. You can email a patch to developers no matter what version control system they're using. As long as your audience can read their email, they can see your edits. Similarly, on your side, all you require is an email account: there's no need to setup an online Git repository. Recall from the first chapter: $ git diff 1b6d > my.patch outputs a patch which can be pasted into an email for discussion. In a Git repository, type: $ git apply < my.patch to apply the patch. In more formal settings, when author names and perhaps signatures should be recorded, generate the corresponding patches past a certain point by typing: $ git format-patch 1b6d The resulting files can be given to *git-send-email*, or sent by hand. You can also specify a range of commits: $ git format-patch 1b6d..HEAD^^ On the receiving end, save an email to a file, then type: $ git am < email.txt This applies the incoming patch and also creates a commit, including information such as the author. With a browser email client, you may need to click a button to see the email in its raw original form before saving the patch to a file. There are slight differences for mbox-based email clients, but if you use one of these, you're probably the sort of person who can figure them out easily without reading tutorials! === Sorry, We've Moved === After cloning a repository, running *git push* or *git pull* will automatically push to or pull from the original URL. How does Git do this? The secret lies in config options created with the clone. Let's take a peek: $ git config --list The +remote.origin.url+ option controls the source URL; ``origin'' is a nickname given to the source repository. As with the ``master'' branch convention, we may change or delete this nickname but there is usually no reason for doing so. If the original repository moves, we can update the URL via: $ git config remote.origin.url git://new.url/proj.git The +branch.master.merge+ option specifies the default remote branch in a *git pull*. During the initial clone, it is set to the current branch of the source repository, so even if the HEAD of the source repository subsequently moves to a different branch, a later pull will faithfully follow the original branch. This option only applies to the repository we first cloned from, which is recorded in the option +branch.master.remote+. If we pull in from other repositories we must explicitly state which branch we want: $ git pull git://example.com/other.git master The above explains why some of our earlier push and pull examples had no arguments. === Remote Branches === When you clone a repository, you also clone all its branches. You may not have noticed this because Git hides them away: you must ask for them specifically. This prevents branches in the remote repository from interfering with your branches, and also makes Git easier for beginners. List the remote branches with: $ git branch -r You should see something like: origin/HEAD origin/master origin/experimental These represent branches and the HEAD of the remote repository, and can be used in regular Git commands. For example, suppose you have made many commits, and wish to compare against the last fetched version. You could search through the logs for the appropriate SHA1 hash, but it's much easier to type: $ git diff origin/HEAD Or you can see what the ``experimental'' branch has been up to: $ git log origin/experimental === Multiple Remotes === Suppose two other developers are working on our project, and we want to keep tabs on both. We can follow more than one repository at a time with: $ git remote add other git://example.com/some_repo.git $ git pull other some_branch Now we have merged in a branch from the second repository, and we have easy access to all branches of all repositories: $ git diff origin/experimental^ other/some_branch~5 But what if we just want to compare their changes without affecting our own work? In other words, we want to examine their branches without having their changes invade our working directory. Then rather than pull, run: $ git fetch # Fetch from origin, the default. $ git fetch other # Fetch from the second programmer. This just fetches histories. Although the working directory remains untouched, we can refer to any branch of any repository in a Git command because we now possess a local copy. Recall that behind the scenes, a pull is simply a *fetch* then *merge*. Usually we *pull* because we want to merge the latest commit after a fetch; this situation is a notable exception. See *git help remote* for how to remove remote repositories, ignore certain branches, and more. === My Preferences === For my projects, I like contributors to prepare repositories from which I can pull. Some Git hosting services let you host your own fork of a project with the click of a button. After I fetch a tree, I run Git commands to navigate and examine the changes, which ideally are well-organized and well-described. I merge my own changes, and perhaps make further edits. Once satisfied, I push to the main repository. Though I infrequently receive contributions, I believe this approach scales well. See http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[this blog post by Linus Torvalds]. Staying in the Git world is slightly more convenient than patch files, as it saves me from converting them to Git commits. Furthermore, Git handles details such as recording the author's name and email address, as well as the time and date, and asks the author to describe their own change. ������gitmagic-20160304/en/preface.txt��������������������������������������������������������������������0000644�0001750�0001750�00000010127�12666307504�015551� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������= Git Magic = Ben Lynn August 2007 == Preface == http://git-scm.com/[Git] is a version control Swiss army knife. A reliable versatile multipurpose revision control tool whose extraordinary flexibility makes it tricky to learn, let alone master. As Arthur C. Clarke observed, any sufficiently advanced technology is indistinguishable from magic. This is a great way to approach Git: newbies can ignore its inner workings and view Git as a gizmo that can amaze friends and infuriate enemies with its wondrous abilities. Rather than go into details, we provide rough instructions for particular effects. After repeated use, gradually you will understand how each trick works, and how to tailor the recipes for your needs. .Translations - link:/\~blynn/gitmagic/intl/zh_cn/[Simplified Chinese]: by JunJie, Meng and JiangWei. Converted to link:/~blynn/gitmagic/intl/zh_tw/[Traditional Chinese] via +cconv -f UTF8-CN -t UTF8-TW+. - link:/~blynn/gitmagic/intl/fr/[French]: by Alexandre Garel, Paul Gaborit, and Nicolas Deram. Also hosted at http://tutoriels.itaapy.com/[itaapy]. - link:/~blynn/gitmagic/intl/de/[German]: by Benjamin Bellee and Armin Stebich; also http://gitmagic.lordofbikes.de/[hosted on Armin's website]. - link:/~blynn/gitmagic/intl/it/[Italian]: by Mattia Rigotti. - link:/~blynn/gitmagic/intl/ko/[Korean]: by Jung-Ho (John) Han; also https://sites.google.com/site/drinkhanjohn/useful-links/[hosted on John's website]. - link:/~blynn/gitmagic/intl/pl/[Polish]: by Damian Michna. - link:/~blynn/gitmagic/intl/pt_br/[Brazilian Portuguese]: by José Inácio Serafini and Leonardo Siqueira Rodrigues. - link:/~blynn/gitmagic/intl/ru/[Russian]: by Tikhon Tarnavsky, Mikhail Dymskov, and others. - link:/~blynn/gitmagic/intl/es/[Spanish]: by Rodrigo Toledo and Ariset Llerena Tapia. - link:/~blynn/gitmagic/intl/uk/[Ukrainian]: by Volodymyr Bodenchuk. - link:/~blynn/gitmagic/intl/vi/[Vietnamese]: by Trần Ngọc Quân; also http://vnwildman.users.sourceforge.net/gitmagic/[hosted on his website]. .Other Editions - link:book.html[Single webpage]: barebones HTML, with no CSS. - link:book.pdf[PDF file]: printer-friendly. - http://packages.debian.org/gitmagic[Debian package], http://packages.ubuntu.com/gitmagic[Ubuntu package]: get a fast and local copy of this site. Handy http://csdcf.stanford.edu/status/[when this server is offline]. - http://www.amazon.com/Git-Magic-Ben-Lynn/dp/1451523343/[Physical book [Amazon.com]]: 64 pages, 15.24cm x 22.86cm, black and white. Handy when there is no electricity. === Thanks! === I'm humbled that so many people have worked on translations of these pages. I greatly appreciate having a wider audience because of the efforts of those named above. Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, Andy Somerville, Ralf Recker, Øyvind A. Holm, Miklos Vajna, Sébastien Hinderer, Thomas Miedema, Joe Malin, Tyler Breisacher, Sonia Hamilton, Julian Haagsma, Romain Lespinasse, Sergey Litvinov, Oliver Ferrigni, David Toca, Сергей Сергеев, Joël Thieffry, and Baiju Muthukadan contributed corrections and improvements. François Marier maintains the Debian package originally created by Daniel Baumann. My gratitude goes to many others for your support and praise. I'm tempted to quote you here, but it might raise expectations to ridiculous heights. If I've left you out by mistake, please tell me or just send me a patch! === License === This guide is released under http://www.gnu.org/licenses/gpl-3.0.html[the GNU General Public License version 3]. Naturally, the source is kept in a Git repository, and can be obtained by typing: $ git clone git://repo.or.cz/gitmagic.git # Creates "gitmagic" directory. or from one of the mirrors: $ git clone git://github.com/blynn/gitmagic.git $ git clone git://gitorious.org/gitmagic/mainline.git $ git clone https://code.google.com/p/gitmagic/ $ git clone git://git.assembla.com/gitmagic.git $ git clone git@bitbucket.org:blynn/gitmagic.git GitHub, Assembla, and Bitbucket support private repositories, the latter two for free. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/en/basic.txt����������������������������������������������������������������������0000644�0001750�0001750�00000015716�12666307504�015236� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Basic Tricks == Rather than diving into a sea of Git commands, use these elementary examples to get your feet wet. Despite their simplicity, each of them are useful. Indeed, in my first months with Git I never ventured beyond the material in this chapter. === Saving State === About to attempt something drastic? Before you do, take a snapshot of all files in the current directory with: $ git init $ git add . $ git commit -m "My first backup" Now if your new edits go awry, restore the pristine version: $ git reset --hard To save the state again: $ git commit -a -m "Another backup" === Add, Delete, Rename === The above only keeps track of the files that were present when you first ran *git add*. If you add new files or subdirectories, you'll have to tell Git: $ git add readme.txt Documentation Similarly, if you want Git to forget about certain files: $ git rm kludge.h obsolete.c $ git rm -r incriminating/evidence/ Git deletes these files for you if you haven't already. Renaming a file is the same as removing the old name and adding the new name. There's also the shortcut *git mv* which has the same syntax as the *mv* command. For example: $ git mv bug.c feature.c === Advanced Undo/Redo === Sometimes you just want to go back and forget about every change past a certain point because they're all wrong. Then: $ git log shows you a list of recent commits, and their SHA1 hashes: ---------------------------------- commit 766f9881690d240ba334153047649b8b8f11c664 Author: Bob <bob@example.com> Date: Tue Mar 14 01:59:26 2000 -0800 Replace printf() with write(). commit 82f5ea346a2e651544956a8653c0f58dc151275c Author: Alice <alice@example.com> Date: Thu Jan 1 00:00:00 1970 +0000 Initial commit. ---------------------------------- The first few characters of the hash are enough to specify the commit; alternatively, copy and paste the entire hash. Type: $ git reset --hard 766f to restore the state to a given commit and erase all newer commits from the record permanently. Other times you want to hop to an old state briefly. In this case, type: $ git checkout 82f5 This takes you back in time, while preserving newer commits. However, like time travel in a science-fiction movie, if you now edit and commit, you will be in an alternate reality, because your actions are different to what they were the first time around. This alternate reality is called a 'branch', and <<branch,we'll have more to say about this later>>. For now, just remember that $ git checkout master will take you back to the present. Also, to stop Git complaining, always commit or reset your changes before running checkout. To take the computer game analogy again: - *`git reset --hard`*: load an old save and delete all saved games newer than the one just loaded. - *`git checkout`*: load an old game, but if you play on, the game state will deviate from the newer saves you made the first time around. Any saved games you make now will end up in a separate branch representing the alternate reality you have entered. <<branch,We deal with this later>>. You can choose only to restore particular files and subdirectories by appending them after the command: $ git checkout 82f5 some.file another.file Take care, as this form of *checkout* can silently overwrite files. To prevent accidents, commit before running any checkout command, especially when first learning Git. In general, whenever you feel unsure about any operation, Git command or not, first run *git commit -a*. Don't like cutting and pasting hashes? Then use: $ git checkout :/"My first b" to jump to the commit that starts with a given message. You can also ask for the 5th-last saved state: $ git checkout master~5 === Reverting === In a court of law, events can be stricken from the record. Likewise, you can pick specific commits to undo. $ git commit -a $ git revert 1b6d will undo just the commit with the given hash. The revert is recorded as a new commit, which you can confirm by running *git log*. === Changelog Generation === Some projects require a http://en.wikipedia.org/wiki/Changelog[changelog]. Generate one by typing: $ git log > ChangeLog === Downloading Files === Get a copy of a project managed with Git by typing: $ git clone git://server/path/to/files For example, to get all the files I used to create this site: $ git clone git://git.or.cz/gitmagic.git We'll have much to say about the *clone* command soon. === The Bleeding Edge === If you've already downloaded a copy of a project using *git clone*, you can upgrade to the latest version with: $ git pull === Instant Publishing === Suppose you've written a script you'd like to share with others. You could just tell them to download from your computer, but if they do so while you're improving the script or making experimental changes, they could wind up in trouble. Of course, this is why release cycles exist. Developers may work on a project frequently, but they only make the code available when they feel it is presentable. To do this with Git, in the directory where your script resides: $ git init $ git add . $ git commit -m "First release" Then tell your users to run: $ git clone your.computer:/path/to/script to download your script. This assumes they have ssh access. If not, run *git daemon* and tell your users to instead run: $ git clone git://your.computer/path/to/script From now on, every time your script is ready for release, execute: $ git commit -a -m "Next release" and your users can upgrade their version by changing to the directory containing your script and typing: $ git pull Your users will never end up with a version of your script you don't want them to see. === What Have I Done? === Find out what changes you've made since the last commit with: $ git diff Or since yesterday: $ git diff "@{yesterday}" Or between a particular version and 2 versions ago: $ git diff 1b6d "master~2" In each case the output is a patch that can be applied with *git apply*. Try also: $ git whatchanged --since="2 weeks ago" Often I'll browse history with http://sourceforge.net/projects/qgit[qgit] instead, due to its slick photogenic interface, or http://jonas.nitro.dk/tig/[tig], a text-mode interface that works well over slow connections. Alternatively, install a web server, run *git instaweb* and fire up any web browser. === Exercise === Let A, B, C, D be four successive commits where B is the same as A except some files have been removed. We want to add the files back at D. How can we do this? There are at least three solutions. Assuming we are at D: 1. The difference between A and B are the removed files. We can create a patch representing this difference and apply it: $ git diff B A | git apply 2. Since we saved the files back at A, we can retrieve them: $ git checkout A foo.c bar.h 3. We can view going from A to B as a change we want to undo: $ git revert B Which choice is best? Whichever you prefer most. It is easy to get what you want with Git, and often there are many ways to get it. ��������������������������������������������������gitmagic-20160304/en/grandmaster.txt����������������������������������������������������������������0000644�0001750�0001750�00000023232�12666307504�016454� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Git Grandmastery == By now, you should be able to navigate the *git help* pages and understand almost everything. However, pinpointing the exact command required to solve a given problem can be tedious. Perhaps I can save you some time: below are some recipes I have needed in the past. === Source Releases === For my projects, Git tracks exactly the files I'd like to archive and release to users. To create a tarball of the source code, I run: $ git archive --format=tar --prefix=proj-1.2.3/ HEAD === Commit What Changed === Telling Git when you've added, deleted and renamed files is troublesome for certain projects. Instead, you can type: $ git add . $ git add -u Git will look at the files in the current directory and work out the details by itself. Instead of the second add command, run `git commit -a` if you also intend to commit at this time. See *git help ignore* for how to specify files that should be ignored. You can perform the above in a single pass with: $ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove The *-z* and *-0* options prevent ill side-effects from filenames containing strange characters. As this command adds ignored files, you may want to use the `-x` or `-X` option. === My Commit Is Too Big! === Have you neglected to commit for too long? Been coding furiously and forgotten about source control until now? Made a series of unrelated changes, because that's your style? No worries. Run: $ git add -p For each edit you made, Git will show you the hunk of code that was changed, and ask if it should be part of the next commit. Answer with "y" or "n". You have other options, such as postponing the decision; type "?" to learn more. Once you're satisfied, type $ git commit to commit precisely the changes you selected (the 'staged' changes). Make sure you omit the *-a* option, otherwise Git will commit all the edits. What if you've edited many files in many places? Reviewing each change one by one becomes frustratingly mind-numbing. In this case, use *git add -i*, whose interface is less straightforward, but more flexible. With a few keystrokes, you can stage or unstage several files at a time, or review and select changes in particular files only. Alternatively, run *git commit \--interactive* which automatically commits after you're done. === The Index: Git's Staging Area === So far we have avoided Git's famous 'index', but we must now confront it to explain the above. The index is a temporary staging area. Git seldom shuttles data directly between your project and its history. Rather, Git first writes data to the index, and then copies the data in the index to its final destination. For example, *commit -a* is really a two-step process. The first step places a snapshot of the current state of every tracked file into the index. The second step permanently records the snapshot now in the index. Committing without the *-a* option only performs the second step, and only makes sense after running commands that somehow change the index, such as *git add*. Usually we can ignore the index and pretend we are reading straight from and writing straight to the history. On this occasion, we want finer control, so we manipulate the index. We place a snapshot of some, but not all, of our changes into the index, and then permanently record this carefully rigged snapshot. === Don't Lose Your HEAD === The HEAD tag is like a cursor that normally points at the latest commit, advancing with each new commit. Some Git commands let you move it. For example: $ git reset HEAD~3 will move the HEAD three commits back. Thus all Git commands now act as if you hadn't made those last three commits, while your files remain in the present. See the help page for some applications. But how can you go back to the future? The past commits know nothing of the future. If you have the SHA1 of the original HEAD then: $ git reset 1b6d But suppose you never took it down? Don't worry: for commands like these, Git saves the original HEAD as a tag called ORIG_HEAD, and you can return safe and sound with: $ git reset ORIG_HEAD === HEAD-hunting === Perhaps ORIG_HEAD isn't enough. Perhaps you've just realized you made a monumental mistake and you need to go back to an ancient commit in a long-forgotten branch. By default, Git keeps a commit for at least two weeks, even if you ordered Git to destroy the branch containing it. The trouble is finding the appropriate hash. You could look at all the hash values in `.git/objects` and use trial and error to find the one you want. But there's a much easier way. Git records every hash of a commit it computes in `.git/logs`. The subdirectory `refs` contains the history of all activity on all branches, while the file `HEAD` shows every hash value it has ever taken. The latter can be used to find hashes of commits on branches that have been accidentally lopped off. The reflog command provides a friendly interface to these log files. Try $ git reflog Instead of cutting and pasting hashes from the reflog, try: $ git checkout "@{10 minutes ago}" Or checkout the 5th-last visited commit via: $ git checkout "@{5}" See the ``Specifying Revisions'' section of *git help rev-parse* for more. You may wish to configure a longer grace period for doomed commits. For example: $ git config gc.pruneexpire "30 days" means a deleted commit will only be permanently lost once 30 days have passed and *git gc* is run. You may also wish to disable automatic invocations of *git gc*: $ git config gc.auto 0 in which case commits will only be deleted when you run *git gc* manually. === Building On Git === In true UNIX fashion, Git's design allows it to be easily used as a low-level component of other programs, such as GUI and web interfaces, alternative command-line interfaces, patch managements tools, importing and conversion tools and so on. In fact, some Git commands are themselves scripts standing on the shoulders of giants. With a little tinkering, you can customize Git to suit your preferences. One easy trick is to use built-in Git aliases to shorten your most frequently used commands: $ git config --global alias.co checkout $ git config --global --get-regexp alias # display current aliases alias.co checkout $ git co foo # same as 'git checkout foo' Another is to print the current branch in the prompt, or window title. Invoking $ git symbolic-ref HEAD shows the current branch name. In practice, you most likely want to remove the "refs/heads/" and ignore errors: $ git symbolic-ref HEAD 2> /dev/null | cut -b 12- The +contrib+ subdirectory is a treasure trove of tools built on Git. In time, some of them may be promoted to official commands. On Debian and Ubuntu, this directory lives at +/usr/share/doc/git-core/contrib+. One popular resident is +workdir/git-new-workdir+. Via clever symlinking, this script creates a new working directory whose history is shared with the original repository: $ git-new-workdir an/existing/repo new/directory The new directory and the files within can be thought of as a clone, except since the history is shared, the two trees automatically stay in sync. There's no need to merge, push, or pull. === Daring Stunts === These days, Git makes it difficult for the user to accidentally destroy data. But if you know what you are doing, you can override safeguards for common commands. *Checkout*: Uncommitted changes cause checkout to fail. To destroy your changes, and checkout a given commit anyway, use the force flag: $ git checkout -f HEAD^ On the other hand, if you specify particular paths for checkout, then there are no safety checks. The supplied paths are quietly overwritten. Take care if you use checkout in this manner. *Reset*: Reset also fails in the presence of uncommitted changes. To force it through, run: $ git reset --hard 1b6d *Branch*: Deleting branches fails if this causes changes to be lost. To force a deletion, type: $ git branch -D dead_branch # instead of -d Similarly, attempting to overwrite a branch via a move fails if data loss would ensue. To force a branch move, type: $ git branch -M source target # instead of -m Unlike checkout and reset, these two commands defer data destruction. The changes are still stored in the .git subdirectory, and can be retrieved by recovering the appropriate hash from `.git/logs` (see "HEAD-hunting" above). By default, they will be kept for at least two weeks. *Clean*: Some git commands refuse to proceed because they're worried about clobbering untracked files. If you're certain that all untracked files and directories are expendable, then delete them mercilessly with: $ git clean -f -d Next time, that pesky command will work! === Preventing Bad Commits === Stupid mistakes pollute my repositories. Most frightening are missing files due to a forgotten *git add*. Lesser transgressions are trailing whitespace and unresolved merge conflicts: though harmless, I wish these never appeared on the public record. If only I had bought idiot insurance by using a _hook_ to alert me about these problems: $ cd .git/hooks $ cp pre-commit.sample pre-commit # Older Git versions: chmod +x pre-commit Now Git aborts a commit if useless whitespace or unresolved merge conflicts are detected. For this guide, I eventually added the following to the beginning of the *pre-commit* hook to guard against absent-mindedness: if git ls-files -o | grep '\.txt$'; then echo FAIL! Untracked .txt files. exit 1 fi Several git operations support hooks; see *git help hooks*. We activated the sample *post-update* hook earlier when discussing Git over HTTP. This runs whenever the head moves. The sample post-update script updates files Git needs for communication over Git-agnostic transports such as HTTP. ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/en/history.txt��������������������������������������������������������������������0000644�0001750�0001750�00000025057�12666307504�015655� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Lessons of History == A consequence of Git's distributed nature is that history can be edited easily. But if you tamper with the past, take care: only rewrite that part of history which you alone possess. Just as nations forever argue over who committed what atrocity, if someone else has a clone whose version of history differs to yours, you will have trouble reconciling when your trees interact. Some developers strongly feel history should be immutable, warts and all. Others feel trees should be made presentable before they are unleashed in public. Git accommodates both viewpoints. Like cloning, branching, and merging, rewriting history is simply another power Git gives you. It is up to you to use it wisely. === I Stand Corrected === Did you just commit, but wish you had typed a different message? Then run: $ git commit --amend to change the last message. Realized you forgot to add a file? Run *git add* to add it, and then run the above command. Want to include a few more edits in that last commit? Then make those edits and run: $ git commit --amend -a === ... And Then Some === Suppose the previous problem is ten times worse. After a lengthy session you've made a bunch of commits. But you're not quite happy with the way they're organized, and some of those commit messages could use rewording. Then type: $ git rebase -i HEAD~10 and the last 10 commits will appear in your favourite $EDITOR. A sample excerpt: pick 5c6eb73 Added repo.or.cz link pick a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile Older commits precede newer commits in this list, unlike the `log` command. Here, 5c6eb73 is the oldest commit, and 100834f is the newest. Then: - Remove commits by deleting lines. Like the revert command, but off the record: it will be as if the commit never existed. - Reorder commits by reordering lines. - Replace `pick` with: * `edit` to mark a commit for amending. * `reword` to change the log message. * `squash` to merge a commit with the previous one. * `fixup` to merge a commit with the previous one and discard the log message. For example, we might replace the second `pick` with `squash`: pick 5c6eb73 Added repo.or.cz link squash a311a64 Reordered analogies in "Work How You Want" pick 100834f Added push target to Makefile After we save and quit, Git merges a311a64 into 5c6eb73. Thus *squash* merges into the next commit up: think ``squash up''. Git then combines their log messages and presents them for editing. The command *fixup* skips this step; the squashed log message is simply discarded. If you marked a commit with *edit*, Git returns you to the past, to the oldest such commit. You can amend the old commit as described in the previous section, and even create new commits that belong here. Once you're pleased with the ``retcon'', go forward in time by running: $ git rebase --continue Git replays commits until the next *edit*, or to the present if none remain. You can also abandon the rebase with: $ git rebase --abort So commit early and commit often: you can tidy up later with rebase. === Local Changes Last === You're working on an active project. You make some local commits over time, and then you sync with the official tree with a merge. This cycle repeats itself a few times before you're ready to push to the central tree. But now the history in your local Git clone is a messy jumble of your changes and the official changes. You'd prefer to see all your changes in one contiguous section, and after all the official changes. This is a job for *git rebase* as described above. In many cases you can use the *--onto* flag and avoid interaction. Also see *git help rebase* for detailed examples of this amazing command. You can split commits. You can even rearrange branches of a tree. Take care: rebase is a powerful command. For complicated rebases, first make a backup with *git clone*. === Rewriting History === Occasionally, you need the source control equivalent of airbrushing people out of official photos, erasing them from history in a Stalinesque fashion. For example, suppose we intend to release a project, but it involves a file that should be kept private for some reason. Perhaps I left my credit card number in a text file and accidentally added it to the project. Deleting the file is insufficient, for the file can be accessed from older commits. We must remove the file from all commits: $ git filter-branch --tree-filter 'rm top/secret/file' HEAD See *git help filter-branch*, which discusses this example and gives a faster method. In general, *filter-branch* lets you alter large sections of history with a single command. Afterwards, the +.git/refs/original+ directory describes the state of affairs before the operation. Check the filter-branch command did what you wanted, then delete this directory if you wish to run more filter-branch commands. Lastly, replace clones of your project with your revised version if you want to interact with them later. === Making History === [[makinghistory]] Want to migrate a project to Git? If it's managed with one of the more well-known systems, then chances are someone has already written a script to export the whole history to Git. Otherwise, look up *git fast-import*, which reads text input in a specific format to create Git history from scratch. Typically a script using this command is hastily cobbled together and run once, migrating the project in a single shot. As an example, paste the following listing into temporary file, such as `/tmp/history`: ---------------------------------- commit refs/heads/master committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000 data <<EOT Initial commit. EOT M 100644 inline hello.c data <<EOT #include <stdio.h> int main() { printf("Hello, world!\n"); return 0; } EOT commit refs/heads/master committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800 data <<EOT Replace printf() with write(). EOT M 100644 inline hello.c data <<EOT #include <unistd.h> int main() { write(1, "Hello, world!\n", 14); return 0; } EOT ---------------------------------- Then create a Git repository from this temporary file by typing: $ mkdir project; cd project; git init $ git fast-import --date-format=rfc2822 < /tmp/history You can checkout the latest version of the project with: $ git checkout master . The *git fast-export* command converts any repository to the *git fast-import* format, whose output you can study for writing exporters, and also to transport repositories in a human-readable format. Indeed, these commands can send repositories of text files over text-only channels. === Where Did It All Go Wrong? === You've just discovered a broken feature in your program which you know for sure was working a few months ago. Argh! Where did this bug come from? If only you had been testing the feature as you developed. It's too late for that now. However, provided you've been committing often, Git can pinpoint the problem: $ git bisect start $ git bisect bad HEAD $ git bisect good 1b6d Git checks out a state halfway in between. Test the feature, and if it's still broken: $ git bisect bad If not, replace "bad" with "good". Git again transports you to a state halfway between the known good and bad versions, narrowing down the possibilities. After a few iterations, this binary search will lead you to the commit that caused the trouble. Once you've finished your investigation, return to your original state by typing: $ git bisect reset Instead of testing every change by hand, automate the search by running: $ git bisect run my_script Git uses the return value of the given command, typically a one-off script, to decide whether a change is good or bad: the command should exit with code 0 when good, 125 when the change should be skipped, and anything else between 1 and 127 if it is bad. A negative return value aborts the bisect. You can do much more: the help page explains how to visualize bisects, examine or replay the bisect log, and eliminate known innocent changes for a speedier search. === Who Made It All Go Wrong? === Like many other version control systems, Git has a blame command: $ git blame bug.c which annotates every line in the given file showing who last changed it, and when. Unlike many other version control systems, this operation works offline, reading only from local disk. === Personal Experience === In a centralized version control system, history modification is a difficult operation, and only available to administrators. Cloning, branching, and merging are impossible without network communication. So are basic operations such as browsing history, or committing a change. In some systems, users require network connectivity just to view their own changes or open a file for editing. Centralized systems preclude working offline, and need more expensive network infrastructure, especially as the number of developers grows. Most importantly, all operations are slower to some degree, usually to the point where users shun advanced commands unless absolutely necessary. In extreme cases this is true of even the most basic commands. When users must run slow commands, productivity suffers because of an interrupted work flow. I experienced these phenomena first-hand. Git was the first version control system I used. I quickly grew accustomed to it, taking many features for granted. I simply assumed other systems were similar: choosing a version control system ought to be no different from choosing a text editor or web browser. I was shocked when later forced to use a centralized system. A flaky internet connection matters little with Git, but makes development unbearable when it needs to be as reliable as local disk. Additionally, I found myself conditioned to avoid certain commands because of the latencies involved, which ultimately prevented me from following my desired work flow. When I had to run a slow command, the interruption to my train of thought dealt a disproportionate amount of damage. While waiting for server communication to complete, I'd do something else to pass the time, such as check email or write documentation. By the time I returned to the original task, the command had finished long ago, and I would waste more time trying to remember what I was doing. Humans are bad at context switching. There was also an interesting tragedy-of-the-commons effect: anticipating network congestion, individuals would consume more bandwidth than necessary on various operations in an attempt to reduce future delays. The combined efforts intensified congestion, encouraging individuals to consume even more bandwidth next time to avoid even longer delays. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/en/translate.txt������������������������������������������������������������������0000644�0001750�0001750�00000002331�12666307504�016137� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Appendix B: Translating This Guide == I recommend the following steps for translating this guide, so my scripts can quickly produce HTML and PDF versions, and all translations can live in the same repository. Clone the source, then create a directory corresponding to the target language's IETF tag: see http://www.w3.org/International/articles/language-tags/Overview.en.php[the W3C article on internationalization]. For example, English is "en" and Japanese is "ja". In the new directory, and translate the +txt+ files from the "en" subdirectory. For instance, to translate the guide into http://en.wikipedia.org/wiki/Klingon_language[Klingon], you might type: $ git clone git://repo.or.cz/gitmagic.git $ cd gitmagic $ mkdir tlh # "tlh" is the IETF language code for Klingon. $ cd tlh $ cp ../en/intro.txt . $ edit intro.txt # Translate the file. and so on for each text file. Edit the Makefile and add the language code to the `TRANSLATIONS` variable. You can now review your work incrementally: $ make tlh $ firefox book-tlh/index.html Commit your changes often, then let me know when they're ready. GitHub has an interface that facilitates this: fork the "gitmagic" project, push your changes, then ask me to merge. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/en/intro.txt����������������������������������������������������������������������0000644�0001750�0001750�00000014333�12666307504�015302� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Introduction == I'll use an analogy to introduce version control. See http://en.wikipedia.org/wiki/Revision_control[the Wikipedia entry on revision control] for a saner explanation. === Work is Play === I've played computer games almost all my life. In contrast, I only started using version control systems as an adult. I suspect I'm not alone, and comparing the two may make these concepts easier to explain and understand. Think of editing your code, or document, as playing a game. Once you've made a lot of progress, you'd like to save. To do so, you click on the 'Save' button in your trusty editor. But this will overwrite the old version. It's like those old school games which only had one save slot: sure you could save, but you could never go back to an older state. Which was a shame, because your previous save might have been right at an exceptionally fun part of the game that you'd like to revisit one day. Or worse still, your current save is in an unwinnable state, and you have to start again. === Version Control === When editing, you can 'Save As...' a different file, or copy the file somewhere first before saving if you want to savour old versions. You can compress them too to save space. This is a primitive and labour-intensive form of version control. Computer games improved on this long ago, many of them providing multiple automatically timestamped save slots. Let's make the problem slightly tougher. Say you have a bunch of files that go together, such as source code for a project, or files for a website. Now if you want to keep an old version you have to archive a whole directory. Keeping many versions around by hand is inconvenient, and quickly becomes expensive. With some computer games, a saved game really does consist of a directory full of files. These games hide this detail from the player and present a convenient interface to manage different versions of this directory. Version control systems are no different. They all have nice interfaces to manage a directory of stuff. You can save the state of the directory every so often, and you can load any one of the saved states later on. Unlike most computer games, they're usually smart about conserving space. Typically, only a few files change from version to version, and not by much. Storing the differences instead of entire new copies saves room. === Distributed Control === Now imagine a very difficult computer game. So difficult to finish that many experienced gamers all over the world decide to team up and share their saved games to try to beat it. Speedruns are real-life examples: players specializing in different levels of the same game collaborate to produce amazing results. How would you set up a system so they can get at each other's saves easily? And upload new ones? In the old days, every project used centralized version control. A server somewhere held all the saved games. Nobody else did. Every player kept at most a few saved games on their machine. When a player wanted to make progress, they'd download the latest save from the main server, play a while, save and upload back to the server for everyone else to use. What if a player wanted to get an older saved game for some reason? Maybe the current saved game is in an unwinnable state because somebody forgot to pick up an object back in level three, and they want to find the latest saved game where the game can still be completed. Or maybe they want to compare two older saved games to see how much work a particular player did. There could be many reasons to want to see an older revision, but the outcome is the same. They have to ask the central server for that old saved game. The more saved games they want, the more they need to communicate. The new generation of version control systems, of which Git is a member, are known as distributed systems, and can be thought of as a generalization of centralized systems. When players download from the main server they get every saved game, not just the latest one. It's as if they're mirroring the central server. This initial cloning operation can be expensive, especially if there's a long history, but it pays off in the long run. One immediate benefit is that when an old save is desired for any reason, communication with the central server is unnecessary. === A Silly Superstition === A popular misconception is that distributed systems are ill-suited for projects requiring an official central repository. Nothing could be further from the truth. Photographing someone does not cause their soul to be stolen. Similarly, cloning the master repository does not diminish its importance. A good first approximation is that anything a centralized version control system can do, a well-designed distributed system can do better. Network resources are simply costlier than local resources. While we shall later see there are drawbacks to a distributed approach, one is less likely to make erroneous comparisons with this rule of thumb. A small project may only need a fraction of the features offered by such a system, but using systems that scale poorly for tiny projects is like using Roman numerals for calculations involving small numbers. Moreover, your project may grow beyond your original expectations. Using Git from the outset is like carrying a Swiss army knife even though you mostly use it to open bottles. On the day you desperately need a screwdriver you'll be glad you have more than a plain bottle-opener. === Merge Conflicts === For this topic, our computer game analogy becomes too thinly stretched. Instead, let us again consider editing a document. Suppose Alice inserts a line at the beginning of a file, and Bob appends one at the end of his copy. They both upload their changes. Most systems will automatically deduce a reasonable course of action: accept and merge their changes, so both Alice's and Bob's edits are applied. Now suppose both Alice and Bob have made distinct edits to the same line. Then it is impossible to proceed without human intervention. The second person to upload is informed of a _merge conflict_, and must choose one edit over another, or revise the line entirely. More complex situations can arise. Version control systems handle the simpler cases themselves, and leave the difficult cases for humans. Usually their behaviour is configurable. �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������gitmagic-20160304/en/clone.txt����������������������������������������������������������������������0000644�0001750�0001750�00000023556�12666307504�015256� 0����������������������������������������������������������������������������������������������������ustar �sbadia��������������������������sbadia�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������== Cloning Around == In older version control systems, checkout is the standard operation to get files. You retrieve a bunch of files in a particular saved state. In Git and other distributed version control systems, cloning is the standard operation. To get files, you create a 'clone' of the entire repository. In other words, you practically mirror the central server. Anything the main repository can do, you can do. === Sync Computers === I can tolerate making tarballs or using *rsync* for backups and basic syncing. But sometimes I edit on my laptop, other times on my desktop, and the two may not have talked to each other in between. Initialize a Git repository and commit your files on one machine. Then on the other: $ git clone other.computer:/path/to/files to create a second copy of the files and Git repository. From now on, $ git commit -a $ git pull other.computer:/path/to/files HEAD will 'pull' in the state of the files on the other computer into the one you're working on. If you've recently made conflicting edits in the same file, Git will let you know and you should commit again after resolving them. === Classic Source Control === Initialize a Git repository for your files: $ git init $ git add . $ git commit -m "Initial commit" On the central server, initialize a 'bare repository' in some directory: $ mkdir proj.git $ cd proj.git $ git --bare init $ touch proj.git/git-daemon-export-ok Start the Git daemon if necessary: $ git daemon --detach # it may already be running For Git hosting services, follow the instructions to setup the initially empty Git repository. Typically one fills in a form on a webpage. 'Push' your project to the central server with: $ git push central.server/path/to/proj.git HEAD To check out the source, a developer types: $ git clone central.server/path/to/proj.git After making changes, the developer saves changes locally: $ git commit -a To update to the latest version: $ git pull Any merge conflicts should be resolved then committed: $ git commit -a To check in local changes into the central repository: $ git push If the main server has new changes due to activity by other developers, the push fails, and the developer should pull the latest version, resolve any merge conflicts, then try again. Developers must have SSH access for the above pull and push commands. However, anyone can see the source by typing: $ git clone git://central.server/path/to/proj.git The native git protocol is like HTTP: there is no authentication, so anyone can retrieve the project. Accordingly, by default, pushing is forbidden via the git protocol. === Secret Source === For a closed-source project, omit the touch command, and ensure you never create a file named `git-daemon-export-ok`. The repository can no longer be retrieved via the git protocol; only those with SSH access can see it. If all your repos are closed, running the git daemon is unnecessary because all communication occurs via SSH. === Bare repositories === A bare repository is so named because it has no working directory; it only contains files that are normally hidden away in the `.git` subdirectory. In other words, it maintains the history of a project, and never holds a snapshot of any given version. A bare repository plays a role similar to that of the main server in a centralized version control system: the home of your project. Developers clone your project from it, and push the latest official changes to it. Typically it resides on a server that does little else but disseminate data. Development occurs in the clones, so the home repository can do without a working directory. Many Git commands fail on bare repositories unless the `GIT_DIR` environment variable is set to the repository path, or the `--bare` option is supplied. === Push versus pull === Why did we introduce the push command, rather than rely on the familiar pull command? Firstly, pulling fails on bare repositories: instead you must 'fetch', a command we later discuss. But even if we kept a normal repository on the central server, pulling into it would still be cumbersome. We would have to login to the server first, and give the pull command the network address of the machine we're pulling from. Firewalls may interfere, and what if we have no shell access to the server in the first place? However, apart from this case, we discourage pushing into a repository, because confusion can ensue when the destination has a working directory. In short, while learning Git, only push when the target is a bare repository; otherwise pull. === Forking a Project === Sick of the way a project is being run? Think you could do a better job? Then on your server: $ git clone git://main.server/path/to/files Next, tell everyone about your fork of the project at your server. At any later time, you can merge in the changes from the original project with: $ git pull === Ultimate Backups === Want numerous tamper-proof geographically diverse redundant archives? If your project has many developers, don't do anything! Every clone of your code is effectively a backup. Not just of the current state, but of your project's entire history. Thanks to cryptographic hashing, if anyone's clone becomes corrupted, it will be spotted as soon as they try to communicate with others. If your project is not so popular, find as many servers as you can to host clones. The truly paranoid should always write down the latest 20-byte SHA1 hash of the HEAD somewhere safe. It has to be safe, not private. For example, publishing it in a newspaper would work well, because it's hard for an attacker to alter every copy of a newspaper. === Light-Speed Multitask === Say you want to work on several features in parallel. Then commit your project and run: $ git clone . /some/new/directory Thanks to http://en.wikipedia.org/wiki/Hard_link[hardlinking], local clones require less time and space than a plain backup. You can now work on two independent features simultaneously. For example, you can edit one clone while the other is compiling. At any time, you can commit and pull changes from the other clone: $ git pull /the/other/clone HEAD === Guerilla Version Control === Are you working on a project that uses some other version control system, and you sorely miss Git? Then initialize a Git repository in your working directory: $ git init $ git add . $ git commit -m "Initial commit" then clone it: $ git clone . /some/new/directory Now go to the new directory and work here instead, using Git to your heart's content. Once in a while, you'll want to sync with everyone else, in which case go to the original directory, sync using the other version control system, and type: $ git add . $ git commit -m "Sync with everyone else" Then go to the new directory and run: $ git commit -a -m "Description of my changes" $ git pull The procedure for giving your changes to everyone else depends on the other version control system. The new directory contains the files with your changes. Run whatever commands of the other version control system are needed to upload them to the central repository. Subversion, perhaps the best centralized version control system, is used by countless projects. The *git svn* command automates the above for Subversion repositories, and can also be used to http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html[export a Git project to a Subversion repository]. === Mercurial === Mercurial is a similar version control system that can almost seamlessly work in tandem with Git. With the `hg-git` plugin, a Mercurial user can losslessly push to and pull from a Git repository. Obtain the `hg-git` plugin with Git: $ git clone git://github.com/schacon/hg-git.git or Mercurial: $ hg clone http://bitbucket.org/durin42/hg-git/ Sadly, I am unaware of an analogous plugin for Git. For this reason, I advocate Git over Mercurial for the main repository, even if you prefer Mercurial. With a Mercurial project, usually a volunteer maintains a parallel Git repository to accommodate Git users, whereas thanks to the `hg-git` plugin, a Git project automatically accommodates Mercurial users. Although the plugin can convert a Mercurial repository to a Git repository by pushing to an empty repository, this job is easier with the `hg-fast-export.sh` script, available from: $ git clone git://repo.or.cz/fast-export.git To convert, in an empty directory: $ git init $ hg-fast-export.sh -r /hg/repo after adding the script to your `$PATH`. === Bazaar === We briefly mention Bazaar because it is the most popular free distributed version control system after Git and Mercurial. Bazaar has the advantage of hindsight, as it is relatively young; its designers could learn from mistakes of the past, and sidestep minor historical warts. Additionally, its developers are mindful of portability and interoperation with other version control systems. A `bzr-git` plugin lets Bazaar users work with Git repositories to some extent. The `tailor` program converts Bazaar repositories to Git repositories, and can do so incrementally, while `bzr-fast-export` is well-suited for one-shot conversions. === Why I use Git === I originally chose Git because I heard it could manage the unimaginably unmanageable Linux kernel source. I've never felt a need to switch. Git has served admirably, and I've yet to be bitten by its flaws. As I primarily use Linux, issues on other platforms are of no concern. Also, I prefer C programs and bash scripts to executables such as Python scripts: there are fewer dependencies, and I'm addicted to fast running times. I did think about how Git could be improved, going so far as to write my own Git-like tool, but only as an academic exercise. Had I completed my project, I would have stayed with Git anyway, as the gains are too slight to justify using an oddball system. Naturally, your needs and wants likely differ, and you may be better off with another system. Nonetheless, you can't go far wrong with Git. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������