上個週末遇到了一個這樣的場景node
node_modules src - ...files test - test.js package.json
睡前原本準備上傳到github倉庫git
git init
git add -A
發現忘記添加.gitignore
,把node_modules
文件都add進去了
因而手賤輸入了git reset --hard
github
而後發現...目錄裏的東西所有沒了(只剩下.git/
文件架),shell
當時個人心裏
json
心急如焚懊悔不已的我,通過查閱相關資料,仍是找到了一些拯救代碼的方法異步
因爲每次git命令進行操做時git都會對相關文件進行快照,並經過必定形式把信息保存再.git/
目錄下。ui
因爲此前我使用過git add -A
命令,所以當文件被放進暫存區時,快照信息對象就已經保存了,而實用git reset --hard
以後,這些對象就變成了懸空文件對象(dangling blob
)。spa
咱們可使用git fsck
命令顯示他們.net
git fsck:用於驗證當前git倉庫數據的有效性和一致性,可以顯示那些"丟失"的commit
、blob
(文件)、tree
等。3d
咱們能夠經過如下命令git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")
咱們獲得一大堆blob
的hash ID
unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3 unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03 ...
接下來使用git show
就能顯示這些對象的內容了,例如git show 907b308
可是因爲我曾經添加的文件實在太多node_modules
裏的文件可能有上千個,所以對逐個ID進行git show
肉眼篩選是很是不科學。
所以我寫了個簡單的nodejs腳本(由於我比較熟悉),篩選還原那些我須要的文件。
首先使用git fsck
把hash ID都存到一個文件裏git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > allhashes
"use strict"; const fs = require("fs"); const shelljs = require("shelljs"); const through = require("through2"); let buf = fs.readFileSync("./allhashes") buf = buf.toString(); let hashes = [] buf.replace(/dangling blob (\w+)/gi,function (matached, hash) { hashes.push(hash) }); let all = hashes.length; let left = all; hashes.forEach(hash=>{ let fullContent = "" let stdout = shelljs.exec("git show "+hash,{silent:true}).stdout; let input = through(); console.log((left--)+"/"+all); //TODO:through2原來是爲了處理stdout流的異步數據引入的,當前同步過程下不須要 input.pipe(through((buf,_,next)=>{ fullContent = fullContent+buf.toString(); next(null,buf) },flush=>{ if (matchContent(fullContent)){ fs.writeFile("./objects/"+hash,fullContent) } flush() })) input.push(stdout); input.push(null); }) function matchContent(content){ // ... 匹配規則 }
因而通過幾分鐘的執行,我找回了個人代碼