章節 ▾ 第二版

9.2 Git 和其他系統 - 遷移到 Git

遷移到 Git

如果您有使用其他版本控制系統(VCS)的現有程式碼庫,但您已決定開始使用 Git,那麼您必須以某種方式遷移您的專案。本節將介紹一些用於常見系統的匯入器,然後演示如何開發自己的自定義匯入器。您將學習如何從幾個大型專業使用的 SCM 系統匯入資料,因為它們構成了切換使用者的絕大多數,並且因為高質量的工具很容易獲得。

Subversion

如果您閱讀了上一節關於使用 git svn 的內容,您可以輕鬆地使用那些說明來 git svn clone 一個倉庫;然後,停止使用 Subversion 伺服器,推送到新的 Git 伺服器,然後開始使用它。如果您想要歷史記錄,您可以儘快完成,只要您能從 Subversion 伺服器中提取資料(這可能需要一段時間)。

然而,匯入並不完美;而且由於它會花費很長時間,您最好正確進行。第一個問題是作者資訊。在 Subversion 中,每個提交的人在系統上都有一個使用者,該使用者記錄在提交資訊中。上一節中的示例在某些地方顯示了 schacon,例如 blame 輸出和 git svn log。如果您想將其對映到更好的 Git 作者資料,您需要一個從 Subversion 使用者到 Git 作者的對映。建立一個名為 users.txt 的檔案,該檔案具有以下格式的對映

schacon = Scott Chacon <schacon@geemail.com>
selse = Someo Nelse <selse@geemail.com>

要獲取 SVN 使用的作者名稱列表,您可以執行此命令

$ svn log --xml --quiet | grep author | sort -u | \
  perl -pe 's/.*>(.*?)<.*/$1 = /'

這會以 XML 格式生成日誌輸出,然後只保留包含作者資訊的行,丟棄重複項,並剝離 XML 標籤。顯然,這隻在安裝了 grepsortperl 的機器上有效。然後,將該輸出重定向到您的 users.txt 檔案,以便您可以在每個條目旁邊新增等效的 Git 使用者資料。

注意

如果您在 Windows 機器上嘗試此操作,這時您將遇到麻煩。微軟在 https://learn.microsoft.com/en-us/azure/devops/repos/git/perform-migration-from-svn-to-git 提供了很好的建議和示例。

您可以將此檔案提供給 git svn,以幫助它更準確地對映作者資料。您還可以透過將 --no-metadata 傳遞給 cloneinit 命令來告訴 git svn 不要包含 Subversion 通常匯入的元資料。元資料包括 Git 在匯入期間生成的每個提交訊息中的 git-svn-id。這會使您的 Git 日誌膨脹,並且可能使其有點不清楚。

注意

當您想將 Git 倉庫中的提交映象回原始 SVN 倉庫時,您需要保留元資料。如果您不希望在提交日誌中進行同步,請隨意省略 --no-metadata 引數。

這使得您的 import 命令看起來像這樣

$ git svn clone http://my-project.googlecode.com/svn/ \
      --authors-file=users.txt --no-metadata --prefix "" -s my_project
$ cd 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

不僅作者欄位看起來好多了,而且 git-svn-id 也不見了。

您還應該進行一些匯入後的清理工作。首先,您應該清理 git svn 設定的奇怪引用。首先,您將移動標籤,使它們成為實際的標籤而不是奇怪的遠端分支,然後您將移動其餘的分支,使它們成為本地分支。

要將標籤轉換為真正的 Git 標籤,請執行

$ for t in $(git for-each-ref --format='%(refname:short)' refs/remotes/tags); do git tag ${t/tags\//} $t && git branch -D -r $t; done

這會將以 refs/remotes/tags/ 開頭的遠端分支引用轉換為真正的(輕量級)標籤。

接下來,將 refs/remotes 下面的其餘引用移動到本地分支

$ for b in $(git for-each-ref --format='%(refname:short)' refs/remotes); do git branch $b refs/remotes/$b && git branch -D -r $b; done

有時您可能會看到一些以 @xxx(其中 xxx 是一個數字)結尾的額外分支,而在 Subversion 中您只看到一個分支。這實際上是 Subversion 的一項功能,稱為“peg-revisions”,Git 根本沒有語法對應項。因此,git svn 只是在分支名稱中添加了 SVN 版本號,就像您在 SVN 中寫的那樣來引用該分支的 peg-revision。如果您不再關心 peg-revisions,只需刪除它們

$ for p in $(git for-each-ref --format='%(refname:short)' | grep @); do git branch -D $p; done

現在所有舊分支都是真正的 Git 分支,所有舊標籤都是真正的 Git 標籤。

還有最後一件事需要清理。不幸的是,git svn 建立了一個名為 trunk 的額外分支,它對映到 Subversion 的預設分支,但 trunk 引用指向與 master 相同的位置。由於 master 在 Git 中更符合習慣,這裡是如何刪除多餘的分支

$ git branch -d trunk

最後要做的是將您新的 Git 伺服器新增為遠端,並推送到那裡。這是一個將您的伺服器新增為遠端的示例

$ git remote add origin git@my-git-server:myrepository.git

因為您希望所有分支和標籤都能上傳,所以現在您可以執行此命令

$ git push origin --all
$ git push origin --tags

您所有的分支和標籤都應該在您的新 Git 伺服器上,以一個乾淨、漂亮的匯入形式呈現。

Mercurial

由於 Mercurial 和 Git 在表示版本方面具有相當相似的模型,並且 Git 更加靈活,因此使用一個名為“hg-fast-export”的工具將倉庫從 Mercurial 轉換為 Git 相當直接,您需要一個副本

$ git clone https://github.com/frej/fast-export.git

轉換的第一步是獲取您想轉換的 Mercurial 倉庫的完整克隆

$ hg clone <remote repo URL> /tmp/hg-repo

下一步是建立一個作者對映檔案。Mercurial 在允許放入變更集作者欄位的內容方面比 Git 更寬容,所以這是一個清理門戶的好時機。在 bash shell 中,只需一條命令即可生成此檔案

$ 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 允許我們透過將每一行變成一個規則來修復此問題:"<input>"="<output>",將 <input> 對映到 <output>。在 <input><output> 字串中,支援 Python string_escape 編碼理解的所有轉義序列。如果作者對映檔案中沒有匹配的 <input>,則該作者將保持不變地傳送到 Git。如果所有使用者名稱看起來都沒問題,那麼我們根本不需要這個檔案。在此示例中,我們希望我們的檔案看起來像這樣

"bob"="Bob Jones <bob@company.com>"
"bob@localhost"="Bob Jones <bob@company.com>"
"bob <bob@company.com>"="Bob Jones <bob@company.com>"
"bob jones <bob <AT> company <DOT> com>"="Bob Jones <bob@company.com>"

當 Mercurial 的名稱不被 Git 允許時,可以使用相同型別的對映檔案來重新命名分支和標籤。

下一步是建立我們的新 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 標誌告訴它在哪裡找到作者對映檔案(分支和標籤對映檔案分別由 -B-T 標誌指定)。該指令碼解析 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 中所述),然後克隆倉庫。Git Fusion 會讓您得到一個看起來像原生 Git 倉庫的東西,然後就可以根據需要推送到原生 Git 主機了。如果您願意,甚至可以使用 Perforce 作為您的 Git 主機。

Git-p4

Git-p4 也可以充當匯入工具。例如,我們將從 Perforce Public Depot 匯入 Jam 專案。要設定您的客戶端,您必須將 P4PORT 環境變數匯出到 Perforce 倉庫

$ export P4PORT=public.perforce.com:1666
注意

為了能夠跟隨,您將需要一個 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 來匯入專案的所有分支。有關更多詳細資訊,請參閱 分支

此時您幾乎完成了。如果您轉到 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 伺服器。

自定義匯入器

如果您的系統不是上述之一,您應該線上查詢匯入器——對於許多其他系統,包括 CVS、Clear Case、Visual Source Safe,甚至是存檔目錄,都有高質量的匯入器。如果這些工具都不適合您,或者您有一個更晦澀的工具,或者您需要一個更自定義的匯入過程,那麼您應該使用 git fast-import。此命令從 stdin 讀取簡單的指令來寫入特定的 Git 資料。用這種方式建立 Git 物件比執行原始 Git 命令或嘗試寫入原始物件要容易得多(有關更多資訊,請參閱 Git Internals)。這樣,您可以編寫一個匯入指令碼,從您要匯入的系統中讀取必要的資訊,並將直接的指令列印到 stdout。然後,您可以執行此程式並透過 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 內容快照是什麼,提交資料指向哪裡,以及它們的順序。您的策略將是逐個快照地進行,並建立包含每個目錄內容的提交,並將每個提交連結回前一個。

正如我們在 An Example Git-Enforced Policy 中所做的那樣,我們將用 Ruby 編寫,因為它通常是我們使用的語言,並且易於閱讀。您可以用您熟悉的任何語言編寫這個示例——它只需要將適當的資訊列印到 stdout。而且,如果您在 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)

格式由單詞 data、要讀取的資料的大小、換行符,最後是資料組成。因為您需要使用相同的格式來指定檔案內容,所以您建立了一個幫助方法 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 手冊頁以瞭解如何以這種方式提供資料的詳細資訊。

列出新檔案內容或指定已修改檔案及其新內容的格式如下

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
注意

如果您在 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 目錄中提供了許多更復雜場景的示例。