# 遷移到 Git
如果你現在有一個正在使用其他 VCS 的代碼庫,但是你已經決定開始使用 Git,必須通過某種方式將你的項目遷移至 Git。 這一部分會介紹一些通用系統的導入器,然后演示如何開發你自己定制的導入器。 你將會學習如何從幾個大型專業應用的 SCM 系統中導入數據,不僅因為它們是大多數想要轉換的用戶正在使用的系統,也因為獲取針對它們的高質量工具很容易。
## Subversion
如果你閱讀過前面關于?`git svn`?的章節,可以輕松地使用那些指令來?`git svn clone`?一個倉庫,停止使用 Subversion 服務器,推送到一個新的 Git 服務器,然后就可以開始使用了。 如果你想要歷史,可以從 Subversion 服務器上盡可能快地拉取數據來完成這件事(這可能會花費一些時間)。
然而,導入并不完美;因為花費太長時間了,你可能早已用其他方法完成導入操作。 導入產生的第一個問題就是作者信息。 在 Subversion 中,每一個人提交時都需要在系統中有一個用戶,它會被記錄在提交信息內。 在之前章節的例子中幾個地方顯示了?`schacon`,比如?`blame`?輸出與?`git svn log`。 如果想要將上面的 Subversion 用戶映射到一個更好的 Git 作者數據中,你需要一個 Subversion 用戶到 Git 用戶的映射。 創建一個?`users.txt`?的文件包含像下面這種格式的映射:
~~~
schacon = Scott Chacon <schacon@geemail.com>
selse = Someo Nelse <selse@geemail.com>
~~~
為了獲得 SVN 使用的作者名字列表,可以運行這個:
~~~
$ svn log --xml | grep author | sort -u | \
perl -pe 's/.*>(.*?)<.*/$1 = /'
~~~
這會將日志輸出為 XML 格式,然后保留作者信息行、去除重復、去除 XML 標記。 (很顯然這只會在安裝了?`grep`、`sort`?與?`perl`?的機器上運行。) 然后,將輸出重定向到你的 users.txt 文件中,這樣就可以在每一個記錄后面加入對應的 Git 用戶數據。
你可以將此文件提供給?`git svn`?來幫助它更加精確地映射作者數據。 也可以通過傳遞?`--no-metadata`?給?`clone`?與?`init`?命令,告訴?`git svn`?不要包括 Subversion 通常會導入的元數據。 這會使你的?`import`?命令看起來像這樣:
~~~
$ git svn clone http://my-project.googlecode.com/svn/ \
--authors-file=users.txt --no-metadata -s my_project
~~~
現在在?`my_project`?目錄中應當有了一個更好的 Subversion 導入。 并不像是下面這樣的提交:
~~~
commit 37efa680e8473b615de980fa935944215428a35a
Author: schacon <schacon@4c93b258-373f-11de-be05-5f7a86268029>
Date: Sun May 3 00:12:22 2009 +0000
fixed install - go to trunk
git-svn-id: https://my-project.googlecode.com/svn/trunk@94 4c93b258-373f-11de-
be05-5f7a86268029
~~~
反而它們看起來像是這樣:
~~~
commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2
Author: Scott Chacon <schacon@geemail.com>
Date: Sun May 3 00:12:22 2009 +0000
fixed install - go to trunk
~~~
不僅是 Author 字段更好看了,`git-svn-id`?也不在了。
之后,你應當做一些導入后的清理工作。 第一步,你應當清理?`git svn`?設置的奇怪的引用。 首先移動標簽,這樣它們就是標簽而不是奇怪的遠程引用,然后你會移動剩余的分支這樣它們就是本地的了。
為了將標簽變為合適的 Git 標簽,運行
~~~
$ cp -Rf .git/refs/remotes/origin/tags/* .git/refs/tags/
$ rm -Rf .git/refs/remotes/origin/tags
~~~
這會使原來在?`remotes/origin/tags/`?里的遠程分支引用變成真正的(輕量)標簽。
接下來,將?`refs/remotes`?下剩余的引用移動為本地分支:
~~~
$ cp -Rf .git/refs/remotes/* .git/refs/heads/
$ rm -Rf .git/refs/remotes
~~~
現在所有的舊分支都是真正的 Git 分支,并且所有的舊標簽都是真正的 Git 標簽。 最后一件要做的事情是,將你的新 Git 服務器添加為遠程倉庫并推送到上面。 下面是一個將你的服務器添加為遠程倉庫的例子:
~~~
$ git remote add origin git@my-git-server:myrepository.git
~~~
因為想要上傳所有分支與標簽,你現在可以運行:
~~~
$ git push origin --all
~~~
通過以上漂亮、干凈地導入操作,你的所有分支與標簽都應該在新 Git 服務器上。
## Mercurial
因為 Mercurial 與 Git 在表示版本時有著非常相似的模型,也因為 Git 擁有更加強大的靈活性,將一個倉庫從 Mercurial 轉換到 Git 是相當直接的,使用一個叫作“hg-fast-export”的工具,需要從這里拷貝一份:
~~~
$ git clone http://repo.or.cz/r/fast-export.git /tmp/fast-export
~~~
轉換的第一步就是要先得到想要轉換的 Mercurial 倉庫的完整克隆:
~~~
$ hg clone <remote repo URL> /tmp/hg-repo
~~~
下一步就是創建一個作者映射文件。 Mercurial 對放入到變更集作者字段的內容比 Git 更寬容一些,所以這是一個清理的好機會。 只需要用到?`bash`?終端下的一行命令:
~~~
$ cd /tmp/hg-repo
$ hg log | grep user: | sort | uniq | sed 's/user: *//' > ../authors
~~~
這會花費幾秒鐘,具體要看項目提交歷史有多少,最終?`/tmp/authors`?文件看起來會像這樣:
~~~
bob
bob@localhost
bob <bob@company.com>
bob jones <bob <AT> company <DOT> com>
Bob Jones <bob@company.com>
Joe Smith <joe@company.com>
~~~
在這個例子中,同一個人(Bob)使用不同的名字創建變更集,其中一個實際上是正確的,另一個完全不符合 Git 提交的規范。 Hg-fast-export 通過向我們想要修改的行尾添加?`={new name and email address}`?來修正這個問題,移除任何我們想要保留的用戶名所在的行。 如果所有的用戶名看起來都是正確的,那我們根本就不需要這個文件。 在本例中,我們會使文件看起來像這樣:
~~~
bob=Bob Jones <bob@company.com>
bob@localhost=Bob Jones <bob@company.com>
bob jones <bob <AT> company <DOT> com>=Bob Jones <bob@company.com>
bob <bob@company.com>=Bob Jones <bob@company.com>
~~~
下一步是創建一個新的 Git 倉庫,然后運行導出腳本:
~~~
$ git init /tmp/converted
$ cd /tmp/converted
$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors
~~~
`-r`?選項告訴 hg-fast-export 去哪里尋找我們想要轉換的 Mercurial 倉庫,`-A`?標記告訴它在哪找到作者映射文件。 這個腳本會分析 Mercurial 變更集然后將它們轉換成 Git“fast-import”功能(我們將在之后詳細討論)需要的腳本。 這會花一點時間(盡管它比通過網格?*更*?快),輸出相當的冗長:
~~~
$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors
Loaded 4 authors
master: Exporting full revision 1/22208 with 13/0/0 added/changed/removed files
master: Exporting simple delta revision 2/22208 with 1/1/0 added/changed/removed files
master: Exporting simple delta revision 3/22208 with 0/1/0 added/changed/removed files
[…]
master: Exporting simple delta revision 22206/22208 with 0/4/0 added/changed/removed files
master: Exporting simple delta revision 22207/22208 with 0/2/0 added/changed/removed files
master: Exporting thorough delta revision 22208/22208 with 3/213/0 added/changed/removed files
Exporting tag [0.4c] at [hg r9] [git :10]
Exporting tag [0.4d] at [hg r16] [git :17]
[…]
Exporting tag [3.1-rc] at [hg r21926] [git :21927]
Exporting tag [3.1] at [hg r21973] [git :21974]
Issued 22315 commands
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects: 120000
Total objects: 115032 ( 208171 duplicates )
blobs : 40504 ( 205320 duplicates 26117 deltas of 39602 attempts)
trees : 52320 ( 2851 duplicates 47467 deltas of 47599 attempts)
commits: 22208 ( 0 duplicates 0 deltas of 0 attempts)
tags : 0 ( 0 duplicates 0 deltas of 0 attempts)
Total branches: 109 ( 2 loads )
marks: 1048576 ( 22208 unique )
atoms: 1952
Memory total: 7860 KiB
pools: 2235 KiB
objects: 5625 KiB
---------------------------------------------------------------------
pack_report: getpagesize() = 4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit = 8589934592
pack_report: pack_used_ctr = 90430
pack_report: pack_mmap_calls = 46771
pack_report: pack_open_windows = 1 / 1
pack_report: pack_mapped = 340852700 / 340852700
---------------------------------------------------------------------
$ git shortlog -sn
369 Bob Jones
365 Joe Smith
~~~
那看起來非常好。 所有 Mercurial 標簽都已被轉換成 Git 標簽,Mercurial 分支與書簽都被轉換成 Git 分支。 現在已經準備好將倉庫推送到新的服務器那邊:
~~~
$ git remote add origin git@my-git-server:myrepository.git
$ git push origin --all
~~~
## Perforce
下一個將要看到導入的系統是 Perforce。 就像我們之前討論過的,有兩種方式讓 Git 與 Perforce 互相通信:git-p4 與 Perforce Git Fusion。
#### Perforce Git Fusion
Git Fusion 使這個過程毫無痛苦。 只需要使用在?[Git Fusion](http://git-scm.com/book/zh/v2/ch00/_p4_git_fusion)?中討論過的配置文件來配置你的項目設置、用戶映射與分支,然后克隆整個倉庫。 Git Fusion 讓你處在一個看起來像是原生 Git 倉庫的環境中,如果愿意的話你可以隨時將它推送到一個原生 Git 托管中。 如果你喜歡的話甚至可以使用 Perforce 作為你的 Git 托管。
#### Git-p4
Git-p4 也可以作為一個導入工具。 作為例子,我們將從 Perforce 公開倉庫中導入 Jam 項目。 為了設置客戶端,必須導出 P4PORT 環境變量指向 Perforce 倉庫:
~~~
$ export P4PORT=public.perforce.com:1666
~~~
###### NOTE
為了繼續后續步驟,需要連接到 Perforce 倉庫。 在我們的例子中將會使用在 public.perforce.com 的公開倉庫,但是你可以使用任何你有權限的倉庫。
運行?`git p4 clone`?命令從 Perforce 服務器導入 Jam 項目,提供倉庫、項目路徑與你想要存放導入項目的路徑:
~~~
$ git-p4 clone //guest/perforce_software/jam@all p4import
Importing from //guest/perforce_software/jam@all into p4import
Initialized empty Git repository in /private/tmp/p4import/.git/
Import destination: refs/remotes/p4/master
Importing revision 9957 (100%)
~~~
這個特定的項目只有一個分支,但是如果你在分支視圖(或者說一些目錄)中配置了一些分支,你可以將?`--detect-branches`?選項傳遞給?`git p4 clone`?來導入項目的所有分支。 查看?[分支](http://git-scm.com/book/zh/v2/ch00/_git_p4_branches)?來了解關于這點的更多信息。
此時你幾乎已經完成了。 如果進入?`p4import`?目錄中并運行?`git log`,可以看到你的導入工作:
~~~
$ git log -2
commit e5da1c909e5db3036475419f6379f2c73710c4e6
Author: giles <giles@giles@perforce.com>
Date: Wed Feb 8 03:13:27 2012 -0800
Correction to line 355; change </UL> to </OL>.
[git-p4: depot-paths = "//public/jam/src/": change = 8068]
commit aa21359a0a135dda85c50a7f7cf249e4f7b8fd98
Author: kwirth <kwirth@perforce.com>
Date: Tue Jul 7 01:35:51 2009 -0800
Fix spelling error on Jam doc page (cummulative -> cumulative).
[git-p4: depot-paths = "//public/jam/src/": change = 7304]
~~~
你可以看到?`git-p4`在每一個提交里都留下了一個標識符。 如果之后想要引用 Perforce 的修改序號的話,標識符保留在那里也是可以的。 然而,如果想要移除標識符,現在正是這么做的時候 - 在你開始在新倉庫中工作之前。?可以使用?`git filter-branch`?將全部標識符移除。
~~~
$ git filter-branch --msg-filter 'sed -e "/^\[git-p4:/d"'
Rewrite e5da1c909e5db3036475419f6379f2c73710c4e6 (125/125)
Ref 'refs/heads/master' was rewritten
~~~
如果運行?`git log`,你會看到所有提交的 SHA-1 校驗和都改變了,但是提交信息中不再有?`git-p4`?字符串了:
~~~
$ git log -2
commit b17341801ed838d97f7800a54a6f9b95750839b7
Author: giles <giles@giles@perforce.com>
Date: Wed Feb 8 03:13:27 2012 -0800
Correction to line 355; change </UL> to </OL>.
commit 3e68c2e26cd89cb983eb52c024ecdfba1d6b3fff
Author: kwirth <kwirth@perforce.com>
Date: Tue Jul 7 01:35:51 2009 -0800
Fix spelling error on Jam doc page (cummulative -> cumulative).
~~~
現在導入已經準備好推送到你的新 Git 服務器上了。
## TFS
如果你的團隊正在將他們的源代碼管理從 TFVC 轉換為 Git,你們會想要最高程度的無損轉換。 這意味著,雖然我們在之前的交互章節介紹了 git-tfs 與 git-tf 兩種工具,但是我們在本部分只能介紹 git-tfs,因為 git-tfs 支持分支,而使用 git-tf 代價太大。
###### NOTE
這是一個單向轉換。 這意味著 Git 倉庫無法連接到原始的 TFVC 項目。
第一件事是映射用戶名。 TFVC 對待變更集作者字段的內容相當寬容,但是 Git 需要人類可讀的名字與郵箱地址。 可以通過?`tf`?命令行客戶端來獲取這個信息,像這樣:
~~~
PS> tf history $/myproject -recursive > AUTHORS_TMP
~~~
這會將歷史中的所有變更集抓取下來并放到 AUTHORS_TMP 文件中,然后我們將會將?`User`?列(第二個)取出來。 打開文件找到列開始與結束的字符并替換,在下面的命令行中,`cut`?命令的參數?`11-20`?就是我們找到的:
~~~
PS> cat AUTHORS_TMP | cut -b 11-20 | tail -n+3 | uniq | sort > AUTHORS
~~~
`cut`?命令只會保留每行中第 11 個到第 22 個字符。?`tail`?命令會跳過前兩行,就是字段表頭與 ASCII 風格的下劃線。 所有這些的結果通過管道送到?`uniq`?來去除重復,然后保存到?`AUTOHRS`文件中。 下一步是手動的;為了讓 git-tfs 有效地使用這個文件,每一行必須是這種格式:
~~~
DOMAIN\username = User Name <email@address.com>
~~~
左邊的部分是 TFVC 中的 “User” 字段,等號右邊的部分是將被用作 Git 提交的用戶名。
一旦有了這個文件,下一件事就是生成一個你需要的 TFVC 項目的完整克隆:
~~~
PS> git tfs clone --with-branches --authors=AUTHORS https://username.visualstudio.com/DefaultCollection $/project/Trunk project_git
~~~
接下來要從提交信息底部清理?`git-tfs-id`?區塊。 下面的命令會完成這個任務:
~~~
PS> git filter-branch -f --msg-filter 'sed "s/^git-tfs-id:.*$//g"' -- --all
~~~
那會使用 Git 終端環境中的?`sed`?命令來將所有以 “git-tfs-id:” 開頭的行替換為 Git 會忽略的空白。
全部完成后,你就已經準備好去增加一個新的遠程倉庫,推送你所有的分支上去,然后你的團隊就可以開始用 Git 工作了。
## 一個自定義的導入器
如果你的系統不是上述中的任何一個,你需要在線查找一個導入器 - 針對許多其他系統有很多高質量的導入器,包括 CVS、Clear Case、Visual Source Safe,甚至是一個檔案目錄。 如果沒有一個工具適合你,需要一個不知名的工具,或者需要更大自由度的自定義導入過程,應當使用?`git fast-import`。 這個命令從標準輸入中讀取簡單指令來寫入特定的 Git 數據。 通過這種方式創建 Git 對象比運行原始 Git 命令或直接寫入原始對象(查看?[Git 內部原理](http://git-scm.com/book/zh/v2/1-git-internals/_git_internals)?了解更多內容)更容易些。 通過這種方式你可以編寫導入腳本,從你要導入的系統中讀取必要數據,然后直接打印指令到標準輸出。 然后可以運行這個程序并通過?`git fast-import`?重定向管道輸出。
為了快速演示,我們會寫一個簡單的導入器。 假設你在?`current`?工作,有時候會備份你的項目到時間標簽?`back_YYYY_MM_DD`?備份目錄中,你想要將這些導入到 Git 中。 目錄結構看起來是這樣:
~~~
$ ls /opt/import_from
back_2014_01_02
back_2014_01_04
back_2014_01_14
back_2014_02_03
current
~~~
為了導入一個 Git 目錄,需要了解 Git 如何存儲它的數據。 你可能記得,Git 在底層存儲指向內容快照的提交對象的鏈表。 所有要做的就是告訴?`fast-import`?哪些內容是快照,哪個提交數據指向它們,以及它們進入的順序。 你的策略是一次訪問一個快照,然后用每個目錄中的內容創建提交,并且將每一個提交與前一個連接起來。
如同我們在?[使用強制策略的一個例子](http://git-scm.com/book/zh/v2/1-customizing-git/_an_example_git_enforced_policy)?里做的,我們將會使用 Ruby 寫這個,因為它是我們平常工作中使用的并且它很容易讀懂。 可以使用任何你熟悉的東西來非常輕松地寫這個例子 - 它只需要將合適的信息打印到?`標準輸出`。 然而,如果你在 Windows 上,這意味著需要特別注意不要引入回車符到行尾 - git fast-import 非常特別地只接受換行符(LF)而不是 Windows 使用的回車換行符(CRLF)。
現在開始,需要進入目標目錄中并識別每一個子目錄,每一個都是你要導入為提交的快照。 要進入到每個子目錄中并為導出它打印必要的命令。 基本主循環像這個樣子:
~~~
last_mark = nil
# loop through the directories
Dir.chdir(ARGV[0]) do
Dir.glob("*").each do |dir|
next if File.file?(dir)
# move into the target directory
Dir.chdir(dir) do
last_mark = print_export(dir, last_mark)
end
end
end
~~~
在每個目錄內運行?`print_export`,將會拿到清單并標記之前的快照,然后返回清單并標記現在的快照;通過這種方式,可以將它們合適地連接在一起。 “標記” 是一個給提交標識符的?`fast-import`?術語;當你創建提交,為每一個提交賦予一個標記來將它與其他提交連接在一起。 這樣,在你的?`print_export`?方法中第一件要做的事就是從目錄名字生成一個標記:
~~~
mark = convert_dir_to_mark(dir)
~~~
可以創建一個目錄的數組并使用索引做為標記,因為標記必須是一個整數。 方法類似這樣:
~~~
$marks = []
def convert_dir_to_mark(dir)
if !$marks.include?(dir)
$marks << dir
end
($marks.index(dir) + 1).to_s
end
~~~
既然有一個整數代表你的提交,那還要給提交元數據一個日期。 因為目錄名字表達了日期,所以你將會從中解析出日期。 你的?`print_export`?文件的下一行是
~~~
date = convert_dir_to_date(dir)
~~~
`convert_dir_to_date`?定義為
~~~
def convert_dir_to_date(dir)
if dir == 'current'
return Time.now().to_i
else
dir = dir.gsub('back_', '')
(year, month, day) = dir.split('_')
return Time.local(year, month, day).to_i
end
end
~~~
那會返回每一個目錄日期的整數。 最后一項每個提交需要的元數據是提交者信息,它將會被硬編碼在全局變量中:
~~~
$author = 'John Doe <john@example.com>'
~~~
現在準備開始為你的導入器打印出提交數據。 初始信息聲明定義了一個提交對象與它所在的分支,緊接著一個你生成的標記、提交者信息與提交信息、然后是一個之前的提交,如果它存在的話。 代碼看起來像這樣:
~~~
# print the import information
puts 'commit refs/heads/master'
puts 'mark :' + mark
puts "committer #{$author} #{date} -0700"
export_data('imported from ' + dir)
puts 'from :' + last_mark if last_mark
~~~
我們將硬編碼時區信息(-0700),因為這樣很容易。 如果從其他系統導入,必須指定為一個偏移的時區。 提交信息必須指定為特殊的格式:
~~~
data (size)\n(contents)
~~~
這個格式包括文本數據、將要讀取數據的大小、一個換行符、最終的數據。 因為之后還需要為文件內容指定相同的數據格式,你需要創建一個幫助函數,`export_data`:
~~~
def export_data(string)
print "data #{string.size}\n#{string}"
end
~~~
剩下的工作就是指定每一個快照的文件內容。 這很輕松,因為每一個目錄都是一個快照 - 可以在目錄中的每一個文件內容后打印?`deleteall`?命令。 Git 將會適當地記錄每一個快照:
~~~
puts 'deleteall'
Dir.glob("**/*").each do |file|
next if !File.file?(file)
inline_data(file)
end
~~~
注意:因為大多數系統認為他們的版本是從一個提交變化到另一個提交,fast-import 也可以為每一個提交執行命令來指定哪些文件是添加的、刪除的或修改的與新內容是哪些。 可以計算快照間的不同并只提供這些數據,但是這樣做會很復雜 - 也可以把所有數據給 Git 然后讓它為你指出來。 如果這更適合你的數據,查閱?`fast-import`?man 幫助頁來了解如何以這種方式提供你的數據。
這種列出新文件內容或用新內容指定修改文件的格式如同下面的內容:
~~~
M 644 inline path/to/file
data (size)
(file contents)
~~~
這里,644 是模式(如果你有可執行文件,反而你需要檢測并指定 755),inline 表示將會立即把內容放在本行之后。 你的?`inline_data`?方法看起來像這樣:
~~~
def inline_data(file, code = 'M', mode = '644')
content = File.read(file)
puts "#{code} #{mode} inline #{file}"
export_data(content)
end
~~~
可以重用之前定義的?`export_data`?方法,因為它與你定義的提交信息數據的方法一樣。
最后一件你需要做的是返回當前的標記以便它可以傳給下一個迭代:
~~~
return mark
~~~
###### NOTE
如果在 Windows 上還需要確保增加一個額外步驟。 正如之前提到的,Windows 使用 CRLF 作為換行符而 git fast-import 只接受 LF。 為了修正這個問題使 git fast-import 正常工作,你需要告訴 ruby 使用 LF 代替 CRLF:
~~~
$stdout.binmode
~~~
就是這樣。 這是全部的腳本:
~~~
#!/usr/bin/env ruby
$stdout.binmode
$author = "John Doe <john@example.com>"
$marks = []
def convert_dir_to_mark(dir)
if !$marks.include?(dir)
$marks << dir
end
($marks.index(dir)+1).to_s
end
def convert_dir_to_date(dir)
if dir == 'current'
return Time.now().to_i
else
dir = dir.gsub('back_', '')
(year, month, day) = dir.split('_')
return Time.local(year, month, day).to_i
end
end
def export_data(string)
print "data #{string.size}\n#{string}"
end
def inline_data(file, code='M', mode='644')
content = File.read(file)
puts "#{code} #{mode} inline #{file}"
export_data(content)
end
def print_export(dir, last_mark)
date = convert_dir_to_date(dir)
mark = convert_dir_to_mark(dir)
puts 'commit refs/heads/master'
puts "mark :#{mark}"
puts "committer #{$author} #{date} -0700"
export_data("imported from #{dir}")
puts "from :#{last_mark}" if last_mark
puts 'deleteall'
Dir.glob("**/*").each do |file|
next if !File.file?(file)
inline_data(file)
end
mark
end
# Loop through the directories
last_mark = nil
Dir.chdir(ARGV[0]) do
Dir.glob("*").each do |dir|
next if File.file?(dir)
# move into the target directory
Dir.chdir(dir) do
last_mark = print_export(dir, last_mark)
end
end
end
~~~
如果運行這個腳本,你會得到類似下面的內容:
~~~
$ ruby import.rb /opt/import_from
commit refs/heads/master
mark :1
committer John Doe <john@example.com> 1388649600 -0700
data 29
imported from back_2014_01_02deleteall
M 644 inline README.md
data 28
# Hello
This is my readme.
commit refs/heads/master
mark :2
committer John Doe <john@example.com> 1388822400 -0700
data 29
imported from back_2014_01_04from :1
deleteall
M 644 inline main.rb
data 34
#!/bin/env ruby
puts "Hey there"
M 644 inline README.md
(...)
~~~
為了運行導入器,將這些輸出用管道重定向到你想要導入的 Git 目錄中的?`git fast-import`。 可以創建一個新的目錄并在其中運行?`git init`?作為開始,然后運行你的腳本:
~~~
$ git init
Initialized empty Git repository in /opt/import_to/.git/
$ ruby import.rb /opt/import_from | git fast-import
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects: 5000
Total objects: 13 ( 6 duplicates )
blobs : 5 ( 4 duplicates 3 deltas of 5 attempts)
trees : 4 ( 1 duplicates 0 deltas of 4 attempts)
commits: 4 ( 1 duplicates 0 deltas of 0 attempts)
tags : 0 ( 0 duplicates 0 deltas of 0 attempts)
Total branches: 1 ( 1 loads )
marks: 1024 ( 5 unique )
atoms: 2
Memory total: 2344 KiB
pools: 2110 KiB
objects: 234 KiB
---------------------------------------------------------------------
pack_report: getpagesize() = 4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit = 8589934592
pack_report: pack_used_ctr = 10
pack_report: pack_mmap_calls = 5
pack_report: pack_open_windows = 2 / 2
pack_report: pack_mapped = 1457 / 1457
---------------------------------------------------------------------
~~~
正如你所看到的,當它成功完成時,它會給你一串關于它完成內容的統計。 這本例中,一共導入了 13 個對象、4 次提交到 1 個分支。 現在,可以運行?`git log`?來看一下你的新歷史:
~~~
$ git log -2
commit 3caa046d4aac682a55867132ccdfbe0d3fdee498
Author: John Doe <john@example.com>
Date: Tue Jul 29 19:39:04 2014 -0700
imported from current
commit 4afc2b945d0d3c8cd00556fbe2e8224569dc9def
Author: John Doe <john@example.com>
Date: Mon Feb 3 01:00:00 2014 -0700
imported from back_2014_02_03
~~~
做得很好 - 一個漂亮、干凈的 Git 倉庫。 要注意的一點是并沒有檢出任何東西 - 一開始你的工作目錄內并沒有任何文件。 為了得到他們,你必須將分支重置到?`master`?所在的地方:
~~~
$ ls
$ git reset --hard master
HEAD is now at 3caa046 imported from current
$ ls
README.md main.rb
~~~
可以通過?`fast-import`?工具做很多事情 - 處理不同模式、二進制數據、多個分支與合并、標簽、進度指示等等。 一些更復雜情形下的例子可以在 Git 源代碼目錄中的?`contrib/fast-import`目錄中找到。
- 前言
- Scott Chacon 序
- Ben Straub 序
- 獻辭
- 貢獻者
- 引言
- 1. 起步
- 1.1 關于版本控制
- 1.2 Git 簡史
- 1.3 Git 基礎
- 1.4 命令行
- 1.5 安裝 Git
- 1.6 初次運行 Git 前的配置
- 1.7 獲取幫助
- 1.8 總結
- 2. Git 基礎
- 2.1 獲取 Git 倉庫
- 2.2 記錄每次更新到倉庫
- 2.3 查看提交歷史
- 2.4 撤消操作
- 2.5 遠程倉庫的使用
- 2.6 打標簽
- 2.7 Git 別名
- 2.8 總結
- 3. Git 分支
- 3.1 分支簡介
- 3.2 分支的新建與合并
- 3.3 分支管理
- 3.4 分支開發工作流
- 3.5 遠程分支
- 3.6 變基
- 3.7 總結
- 4. 服務器上的 Git
- 4.1 協議
- 4.2 在服務器上搭建 Git
- 4.3 生成 SSH 公鑰
- 4.4 配置服務器
- 4.5 Git 守護進程
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方托管的選擇
- 4.10 總結
- 5. 分布式 Git
- 5.1 分布式工作流程
- 5.2 向一個項目貢獻
- 5.3 維護項目
- 5.4 總結
- 6. GitHub
- 6.1 賬戶的創建和配置
- 6.2 對項目做出貢獻
- 6.3 維護項目
- 6.4 管理組織
- 6.5 腳本 GitHub
- 6.6 總結
- 7. Git 工具
- 7.1 選擇修訂版本
- 7.2 交互式暫存
- 7.3 儲藏與清理
- 7.4 簽署工作
- 7.5 搜索
- 7.6 重寫歷史
- 7.7 重置揭密
- 7.8 高級合并
- 7.9 Rerere
- 7.10 使用 Git 調試
- 7.11 子模塊
- 7.12 打包
- 7.13 替換
- 7.14 憑證存儲
- 7.15 總結
- 8. 自定義 Git
- 8.1 配置 Git
- 8.2 Git 屬性
- 8.3 Git 鉤子
- 8.4 使用強制策略的一個例子
- 8.5 總結
- 9. Git 與其他系統
- 9.1 作為客戶端的 Git
- 9.2 遷移到 Git
- 9.3 總結
- 10. Git 內部原理
- 10.1 底層命令和高層命令
- 10.2 Git 對象
- 10.3 Git 引用
- 10.4 包文件
- 10.5 引用規格
- 10.6 傳輸協議
- 10.7 維護與數據恢復
- 10.8 環境變量
- 10.9 總結
- A. 其它環境中的 Git
- A1.1 圖形界面
- A1.2 Visual Studio 中的 Git
- A1.3 Eclipse 中的 Git
- A1.4 Bash 中的 Git
- A1.5 Zsh 中的 Git
- A1.6 Powershell 中的 Git
- A1.7 總結
- B. 將 Git 嵌入你的應用
- A2.1 命令行 Git 方式
- A2.2 Libgit2
- A2.3 JGit
- C. Git 命令
- A3.1 設置與配置
- A3.2 獲取與創建項目
- A3.3 快照基礎
- A3.4 分支與合并
- A3.5 項目分享與更新
- A3.6 檢查與比較
- A3.7 調試
- A3.8 補丁
- A3.9 郵件
- A3.10 外部系統
- A3.11 管理
- A3.12 底層命令