1. 程式人生 > >Git爬坑記

Git爬坑記

當我 基礎 class 基礎知識 三種 實現 index url git log

Git記錄

使用git 也有一段時間了, git的入門級了解也就不再多說, 但平常使用中, 仍然會遇到很多問題, 在此記錄一二.

在查資料的過程中, 發現了兩個比較好的資料:

特別是第二個, 相當詳細的 Git教程, 如果看完的話, 相信對 Git的使用理解已經基本不是問題了.

常用Git命令清單

Git 分支 - 何謂分支

Git基礎命令

git config --global user.name "myname"
git config --global user.email "myemail"

個人覺得, 這兩項配置的是 每次推送時留下的個人信息, 就相當於 註冊賬號時的個人信息一樣, 別人可以通過這個, 區分, 或聯系你, 但至於 email 是否有效, 仍未可知.

新建git倉庫, 到對應目錄下, 有很多次直接復制路勁過來, 是錯誤的, 因為 linux 下, 分隔符為 ‘/‘,而非windows下的 ‘\‘.

cd C:/xx/xx
git init;
git status; #檢查本地 git 狀態

git add: 需要先 add 到暫存區, 然後才能使用 commit進行提交, 在 eclipse裏面的操作就是, add to index (對於新建文件而言) , 然後commit, 在commit的時候, 必須要有 commit 信息, 否則無法提交;

git add file; #多個文件之間以 空格 隔開即可 add . 是當前目錄所有文件

git rm [file1] [file2]; #刪除工作區文件,並將刪除放入 暫存區;

git rm --cached [file]; #停止追蹤指定文件,但該文件會保留在工作區;

git commit -m "git message"; #輸入提交信息即可;

git commit [file] [file1] -m [message] #提交指定文件;

--如果不用Git -a 的話, 必須先 add 後commit;
git commit -a; #提交工作區自上次commit之後的變化,直接到倉庫區;
git commit -a -m "輸入提交信息";

git commit -v; #提交時顯示diff 信息;

git reflog; #查看 git 記錄

git reset --hard "reflog header"; # 每次記錄都有一個 header 碼, 輸入對應的編碼, 則 回退到, 對應的位置;

Git分支, 不得不說, 分支很強大, 也很有意思

git branch; #查看所有分支

git branch -r; #所有遠程分支

git branch -a; #遠程及本地分支

git branch [branch-name] #新建一個分支,但依然停留在當前分支

git checkout -b [branch]; #新建一個分支,並切換到該分支

git branch [branch] [commit]; #在指定的 commit處,新建一個分支;

git checkout [branch-name]; #切換到指定分支,並更新工作區

git merge [branch]; #合並指定分支到當前分支

git branch -d [branch-name] 刪除分支;

git push origin --delete [branch-name];
git branch -dr [remote/branch]; #刪除遠程分支

Git 信息查看

git log; #查看當前分支版本歷史

git log --stat; #顯示commit歷史,以及每次commit發生變更的文件

git log -S [keyword]; #搜索提交歷史,根據關鍵詞

git log --follow [file]; # 顯示某個文件的版本歷史,包括文件改名
git whatchanged [file];

git shortlog -sn; # 顯示所有提交過的用戶,按提交次數排序

Git 遠程

對於部分 需要關閉 SSL驗證;

git clone [url]; #從遠程拉代碼;
#http://username:password@url; 通過這種方式驗證用戶名和密碼;

git remote add [shortname] [url] #增加一個新的遠程倉庫,並命名

git remote show [remote] #顯示某個遠程倉庫的信息

git remote -v; #顯示所有遠程倉庫

git fetch [remote]; #下載遠程倉庫的所有變動, 更新本地庫, 但不更新工作拷貝;

#更新指定文件(待驗證)
git fetch [remote];
git checkout origin/master [file]

git pull [remote] [branch]; # 取回遠程倉庫的變化,並與本地分支合並

git push [remote] [branch]; # 上傳本地指定分支到遠程倉庫

git push [remote] --force; # 強行推送當前分支到遠程倉庫,即使有沖突

git push [remote] --all; # 推送所有分支到遠程倉庫

Git撤銷

git checkout [file]; # 恢復暫存區的指定文件到工作區

git checkout [commit] [file]; #恢復某個commit的指定文件到暫存區和工作區

git checkout .;  #恢復暫存區的所有文件到工作區

git reset [file]; # 重置暫存區的指定文件,與上一次commit保持一致,但工作區不變

git reset --hard; #重置暫存區與工作區,與上一次commit保持一致

git reset [commit]; #重置當前分支的指針為指定commit,同時重置暫存區,但工作區不變

git reset --hard [commit]; #重置當前分支的HEAD為指定commit,同時重置暫存區和工作區,與指定commit一致

 git reset --keep [commit] #重置當前HEAD為指定commit,但保持暫存區和工作區不變

 git stash; #暫時將未提交的變化移除,稍後再移入
 git stash pop; #應該指的是移入

 git revert [commit]; 新建一個commit,用來撤銷指定commit,後者的所有變化都將被前者抵消,並且應用到當前分支

 # 生成一個可供發布的壓縮包
 $ git archive

Git使用

Git基礎知識

在基礎的命令了解之後, 打算對 git 做一個更深入的了解, 形成更清晰的認知;

至於參考資料,則是官方文檔: 起步-Git-基礎

在git中, 首先應該提到的是幾個比較基礎的概念:

文件的三種狀態: 已提交, 已修改, 已暫存;

Git目錄:

每個項目都有一個 Git 目錄(譯註:如果 git clone 出來的話,就是其中 .git 的目錄;如果 git clone --bare 的話,新建的目錄本身就是 Git 目錄。),它是 Git 用來保存元數據和對象數據庫的地方。

該目錄非常重要,每次克隆鏡像倉庫的時候,實際拷貝的就是這個目錄裏面的數據。

Git的工作目錄:

從項目中取出某個版本的所有文件和目錄,用以開始後續工作的叫做工作目錄。

文件的修改, 刪除, 添加等操作都是在這進行的, 在工作目錄進行的操作, 如果沒有提交的話, 則文件是處於 已修改狀態的.

不難看出, 在 Git的工作目錄中, 文件存在兩種狀態, 已跟蹤, 未跟蹤, 在已跟蹤中 又區分為, 已修改 和 已暫存 兩種狀態;

Git的暫存區域:

只不過是個簡單的文件,一般都放在 Git 目錄中。在暫存區域中保存 把已修改的文件放在下次提交時要保存的清單中。

Git的工作流程如下:

  1. 在工作目錄中修改文件
  2. 對修改後的文件進行快照, 然後儲存到暫存區域
  3. 提交更新, 將保存在暫存區域快照永久儲存在Git目錄中

Git初入

  1. Git配置

    git config --system #/etc/gitconfig 文件:系統中對所有用戶都普遍適用的配置
    
    git config --global; #~/.gitconfig 文件:用戶目錄下的配置文件只適用於該用戶。
    
    git config; #.git/config 文件:這裏的配置僅僅針對當前項目有效;每一個級別的配置都會覆蓋上層的相同配置
    
    git config --list; #查看已有的配置
    
    git config user.name; #查看特定配置
  2. Git倉庫初始化

    #對現有項目使用 Git管理
    
    git init;
    
    git clone git://github.com/schacon/grit.git; #克隆遠程倉庫, 同時目錄名為 grit;
    
    git clone git://github.com/schacon/grit.git mygrit; #可以通過這種方式重命名目錄;
    
    git add readme; #使用add 來跟蹤一個文件; #git add 對於未曾跟蹤的文件進行跟蹤, 對於已跟蹤的文件 放入暫存區; 對於合並時 沖突的文件 還能將 文件標記為 已解決等;
    
    #最好在項目一開始的時候就配置好 .gitignore 文件;
    
    #所有空行或者以註釋符號 # 開頭的行都會被 Git 忽略。
    
    #可以使用標準的 glob 模式匹配。
    
    #匹配模式最後跟反斜杠(/)說明要忽略的是目錄。 /在最前面 表示根目錄
    
    #要忽略指定模式以外的文件或目錄,可以在模式前加上驚嘆號(!)取反。

    所謂的 glob 模式是指 shell 所使用的簡化了的正則表達式。

    星號(*)匹配零個或多個任意字符;

    [abc]匹配任何一個列在方括號中的字符(這個例子要麽匹配一個 a,要麽匹配一個 b,要麽匹配一個 c);

    問號(?)只匹配一個任意字符;

    如果在方括號中使用短劃線分隔兩個字符,表示所有在這兩個字符範圍內的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的數字)。

    # 此為註釋 – 將被 Git 忽略
    # 忽略所有 .a 結尾的文件
    *.a
    # 但 lib.a 除外
    !lib.a
    # 僅僅忽略項目根目錄下的 TODO 文件,不包括 subdir/TODO
    /TODO
    # 忽略 build/ 目錄下的所有文件
    build/
    # 會忽略 doc/notes.txt 但不包括 doc/server/arch.txt
    doc/*.txt
    # 忽略 doc/ 目錄下所有擴展名為 txt 的文件
    doc/**/*.txt  # **/ 通配符在 1.82以上版本可以使用;
  3. Git改動查看:

    git diff; #僅顯示還沒有暫存起來的改動
    
    git diff --staged; #已經暫存起來的文件和上次提交時的快照之間的差異
  4. Git跳過使用暫存區:

    git commit -a [-m] [file]; 
  5. Git 移除:
    #在刪除文件之後, 通過 git rm 移除文件, 以後文件就不會進入版本管理了.
    git rm grit.gemspec; #如果刪除之前修改過並且已經放到暫存區域的話,則必須要用強制刪除選項 -f

    #另外一種情況是,我們想把文件從 Git 倉庫中刪除(亦即從暫存區域移除),但仍然希望保留在當前工作目錄中。
    
    git rm --cached readme.txt; 可以使用 glob模式匹配文件名;
  6. Git 重命名文件:

    git mv file_from file_to;
    
    #有時候用其他工具批處理改名的話,要記得在提交前刪除老的文件名,再添加新的文件名。
  7. Git 歷史/記錄

    git log; #可以看到比較詳細的信息;
    
    git -p; #可以看到每次提交更新的內容差異
    
    git -p -2; #僅看最近兩次的更新;
    
    git log -p -1 --word-diff; #進行單詞層面的文件對比;新增加的單詞被 {+ +} 括起來,被刪除的單詞被 [- -] 括起來。
    
    git log --stat; #   僅顯示簡要的增改行數統計

    除此之外, git log的用法還有多種;

    https://git-scm.com/book/zh/v1/Git-基礎-查看提交歷史

  8. Git的撤銷操作

    git commit --amend;

    可以通過這個命令更改上次的操作, 如果上次提交完沒有更改, 可以通過 git commit --amend -m 更改上次提交註釋, 否則的話, 將暫存區的 與 上次提交合並;

    git reset HEAD [file]; #可以取消已暫存的所有文件/指定文件;
    
    git checkout -- [file]; #撤銷工作目錄下文件的更改.
  9. Git遠程倉庫使用

    git remote [-v]; #查看當前配置有哪些遠程倉庫[遠程倉庫地址]
    
    git remote add [shortname] [url]; #添加一個新的遠程倉庫;
    
    git fetch [remote-name]; #通過這個命令抓取遠程倉庫有的, 但本地倉庫沒有的信息;
    
    git pull; #將遠程倉庫代碼更新到本地倉庫 且和 工作目錄下;

    fetch 命令, 類似於 pull功能, 但並不會自動和 當前的工作目錄合並, 需要手工合並, 可以通過這點來實現, 指定文件的 更新;

    git push [remote-name] [url];

    在push的時候, 有一個需要註意的地方, 首先需要在 服務器上有寫的權限, 其次同一時刻沒有其他人在推送數據, 這條命令才會執行; 如果在你推送之前, 已經有其他人在同一分支推送了數據, 就必須先把他們的更新抓取到本地,合並到自己的項目中,然後才可以再次推送。

    git remote show [remote-name]; #查看遠程倉庫詳細信息;
    
    $ git remote show origin
    * remote origin
    URL: [email protected]:defunkt/github.git
    Remote branch merged with ‘git pull‘ while on branch issues
        issues
    Remote branch merged with ‘git pull‘ while on branch master
        master
    New remote branches (next fetch will store in remotes/origin)
        caching
    Stale tracking branches (use ‘git remote prune‘)
        libwalker
        walker2
    Tracked remote branches
        acl
        apiv2
        dashboard2
        issues
        master
        postgres
    Local branch pushed with ‘git push‘
        master:master
    
    #它告訴我們,運行 git push 時缺省推送的分支是什麽(譯註:最後兩行)。
    #它還顯示了有哪些遠端分支還沒有同步到本地(譯註:第六行的 caching 分支).
    #哪些已同步到本地的遠端分支在遠端服務器上已被刪除(譯註:Stale tracking branches 下面的兩個分支)
    #以及運行 git pull 時將自動合並哪些分支(譯註:前四行中列出的 issues 和 master 分支)。

    Git 遠程倉庫重命名:git remote rename [oldname] [newname];

    Git 遠程倉庫刪除: git remote rm [remote-name];

    git自動命令補全: 自動命令補全

Git分支

Git的分支理解:

在 每一個時刻都存在著無限種可能, 一個不經意的念頭, 引領走向截然不同的世界, 一個不經意的巧合, 在時間的節點上 創建出 另一條分支, 另一條時間線, 通向不同的未來.

我們通過 Git branch [branch-name]創建另一條分支, 並可以在這條分支上自由的做任何你想做的事情, 我們是造物主, 因此可以對當前的 分支畫上一個 休止符, 轉而通過 Git checkout [branch-name] 穿梭到另一條分支上, 因為是兩條互不幹擾的時間線, 因此, 無論我們穿梭到 哪一個節點, 看到的永遠是當時 那個 暫停的畫面.

肆意在兩條甚至無數條時間線不停穿梭, 而不會相互影響無疑是一種美妙的體驗, 但如果想要在穿梭的過程中不迷失, 就需要一個恒定的標識 HEAD, 永遠指向當前分支;

如果在新建一條分支的同時 想要切換到對應分支 , 則需要:

git checkout -b [branch-name];

如果想將兩條分支合並, 則:

git merge [branch-name]; #指的是將 分支 合並入當前分支;

如果順著一個分支走下去可以到達另一個分支的話,那麽 Git 在合並兩者時,只會簡單地把指針右移,因為這種單線的歷史分支不存在任何需要解決的分歧,所以這種合並過程可以稱為快進(Fast forward)。

對待非上述情況, 會找到共同的祖先節點進行合並.

但有時候為了版本演進的清晰起見會用:

git merge --no-ff feature-x;

merge 命令,它會把兩個分支最新的快照 以及二者最新的共同祖先進行三方合並,合並的結果是產生一個新的提交對象

git branch -d [branch-name]; #刪除對應分支;

在分支合並的過程中, 難免會出現相互沖突的文件, 對待這樣的情況, 只有身為創建者的你, 可以解決這個問題, 將文件找出來.

通過: git status;任何包含未解決沖突的文件都會以未合並(unmerged)的狀態列出。

修改成你想要的結果, 然後再次 add 以告訴 git 沖突已經被解決了.

Git 分支管理策略參考:

Git分支管理策略

Git的分支管理

隨著項目不斷開發, 分支難免會越來越多, 然而這其中可能有很大一部分, 你再也不會用到;

git branch --merged; [--no-merged]; #篩選出和當前分支已合並 或 未合並的分支; 通過這種方式,然後將 已合並的分支刪除掉;

在刪除掉未合並的分支時, 會報錯, 因為會丟失已修改的數據, 如果仍然要強制性刪除的話, 可以通過 D 來進行強制刪除;

但必須記住的是:

以上分支均為: 本地分支;

Git遠程分支

Git的遠程分支 仍然是一種分支, 不過比較特殊; 當我們采取 git clone 的方式從遠程獲取到 項目的時候, 默認創建 origin master(分別指代[remote-name][branch-name]);

此時在 Git目錄中保存有兩個分支:

origin master 指向 clone 時 遠程倉庫的 master分支的當前位置;

master 指向本地倉庫的分支 位置; 本地 工作目錄的更新 會使得 master 向後移動. 僅有在 進行 fetch 或者 pull 操作的時候, 會將遠程倉庫的最新master 位置更新下來, 同時 fetch操作會更新 本地 Git目錄(但不會更新工作目錄);

而我們可以通過 git remote add [remote-name] [url] 將指定路徑的遠程倉庫 變為 當前本地 Git的 遠程分支;

推送本地分支, 如果要選擇某個分支進行推送:

git push --set-upstream origin readme-edit; #對於自己創建的分支, 將分支推送到 倉庫;

git push origin serverfix; #將分支推送到遠程分支

git push origin [local-branch-name]:[自定義遠程分支名稱];#也可以通過這種方式 在遠程倉庫創建分支;

git fetch origin/[遠程 特定分支名稱, 非默認分支]; #通過這種方式更新本地 Git目錄;

對於一個全新的遠程分支;

git remote add origin [url];

git fetch origin;

git checkout .; #將暫存區的文件 同步到工作區;

#或者

git checkout -b serverfix origin/serverfix; #將遠程分支合並到本地;

#這會切換到新建的 serverfix 本地分支,其內容同遠程分支 origin/serverfix 一致,這樣你就可以在裏面繼續開發了。

在克隆倉庫時,Git 通常會自動創建一個名為 master 的分支來跟蹤 origin/master。這正是 git push 和 git pull 一開始就能正常工作的原因。

當不再需要某個遠程分支(而非遠程倉庫):刪除遠程分支

git push [遠程名]  :[分支名];

git push origin [local-branch-name]:[自定義遠程分支名稱];

對比下來不難發現, 第一句的意思是 提取空白 然後把它變成遠程分支;

Git分支-分支變基

對應的操作是:

git rebase [branch-name];

merge 命令,它會把兩個分支最新的快照以及二者最新的共同祖先進行三方合並,合並的結果是產生一個新的提交對象。

rebase: 回到兩個分支最近的共同祖先,根據當前分支(也就是要進行變基的分支 branch-name) 後續的歷次提交對象, 生成一系列文件補丁,然後以基底分支(也就是主幹分支 master)最後一個提交對象為新的出發點,逐個應用之前準備好的補丁文件,最後會生成一個新的合並提交對象, 從而改寫 branch-name 的提交歷史,使它成為 master 分支的直接下遊.

簡單的來說: 就是 改寫 master分支的提交歷史; 在這個基礎上, 同時可以修改 沖突文件, 使得合並後不沖突, 在 rebase 之後, 再 進行 merge操作即可.

特別有趣的一點是:

rebase 不一定非得根據 分化之前的分支來進行操作;舉個例子如下:

(個人在操作過程中發現與官方文檔有所區別, 在操作過程中, 需要時刻關註當前分支)

#當前在本地中只有 master 分支; 創建並切換到 newbranch分支;
git checkout -b newbranch;

#在文檔中做一些修改之後, 提交操作;
git commit -a -m ‘new branch 提交修改‘;

#在當前分支節點上再創建新的分支,並切換
git checkout -b newbranch1;

#在newbranch1分支上做出兩次修改, 並分別提交;
git add.;
git commit -m ‘添加testrebase.txt文件‘;
git commit -a -m ‘修改 testrebase.txt 文件‘;

#切換到 newbranch 分支做出兩次修改, 並分別提交;
git checkout newbranch;
git add .;
git commit -m ‘添加 test_rabase_newbranch.txt文件‘;
git commit -a -m ‘修改 test_rebase_newbranch.txt 文件‘;

#進行rebase 操作;
git rebase --onto master newbranch newbranch1;

#再切換到 master分支查看 操作記錄;
git checkout master;
git merge newbranch1;
git log;

#以下是操作記錄中的 commit 信息
#修改 testrebase.txt文件
# 添加 testrebase.txt文件
#new master 1

不難發現,在 master 的操作記錄中, 並沒有 ‘new branch 提交修改‘ 這次的操作記錄;

取出 newbranch1 分支,找出 newbranch1 分支和 newbranch 分支的共同祖先之後的變化,然後把它們在 master 上重演一遍;

也就是 在 git commit -a -m ‘new branch 提交修改‘; 操作之後的修改變化;

如果我們再進行的是:

git rebase --onto master newbranch1 newbranch;
git checkout master;
git merge newbranch1;
git log;

#記錄
#修改 test_rebase_newbranch.txt 文件
#添加 test_rabase_newbranch.txt文件
#new branch 提交修改

因為 newbranch1 是在 newbranch 上再次新建出來的分支, 而 newbranch 則與master 是同源的;


一旦分支中的提交對象發布到公共倉庫,就千萬不要對該分支進行變基操作。

至此Git 各種操作 告一段落; 也能進行基本的使用, 如果出現問題在網上查找答案, 不至於知其然而不知其所以然;

Git爬坑記