簡體   English   中英

為什么git pull不更新本地更改的文件?

[英]Why does git pull not update locally changed files?

據我了解,“ git pull”會進行獲取和合並。 對於iOS項目,我使用發布設置配置了設置(證書,配置文件等)。 但是,當在master分支上時,當我執行git pullgit log ,我看到git提交與遠程master分支的最新提交保持最新,但是git diff仍然顯示我的pbxproj文件是不同的(即以及提供的配置文件字符串等(我已在本地配置)。

明確地說,這對我來說是理想的,因為我只希望代碼更改,而不必在每次發布分支之前(在發布到生產環境之前)更新分支和設置概要文件。 但我也想了解git合並的方式。 我期望git將合並我在本地更改的文件和/或表明沖突。 但是沒有發生這樣的事情。

注意:我知道一個“強制更新”,它使我的本地主服務器完全類似於遠程主機,但想了解此操作與常規合並之間的區別。

git會將來自遠程的更改與本地提交的更改合並。 它不會嘗試與您未提交的更改合並,並且如果同一文件中有遠程更改,它將警告您合並將清除您的本地更改並拒絕合並。

更新 -我意識到我並沒有真正回答“為什么”:

區分本地提交的更改和未提交的本地更改的原因是,您始終可以取回本地提交的更改。 他們處於提交狀態,您可以checkout該提交,在該位置創建一個新分支,甚至在需要時將當前分支reset到該位置。 與本地提交的更改合並不會丟失任何內容。

但是您的工作樹(未提交的本地更改)沒有這種保護。 如果git將更改合並到其中,而結果卻不是您想要的,則無法輕松地“退回”到以前的狀態。

我們還可以就與未提交的更改合並所涉及的內容進行技術差異,但是,如果需要與未提交的更改合並,則可以克服那些技術原因。 但是,由於這樣做可能會導致數據丟失,因此最好避免將更改合並到具有本地未提交更改的任何文件中。

據我了解,“ git pull”會進行獲取和合並。

沒錯 更准確地說, git pull實際上運行 git fetch ,然后運行git merge 或者更確切地說,它曾經是過去的字面意思。 在過去的一年左右的時間里,對Git進行了修改,使其可以通過在內部執行獲取和合並步驟來加快git pull速度,而不是在可能的情況下運行單獨的命令-並且您還可以告訴它執行git rebase而不是git merge ,還有其他一些極端情況。 但從本質上講,pull =提取+合並。

棘手的部分是這兩個步驟分別意味着什么 獲取和合並都很復雜,也可能很復雜。

取回步驟比較簡單:您的Git調用其他Git。 您的Git和他們的Git進行了一段簡短的交談,其中他們的Git告訴您的Git有關他們擁有的任何新提交的信息,而您沒有。 您的Git將這些新的提交加載到您的存儲庫中,以便您現在擁有這些提交; 然后,您的Git會設置您的遠程跟蹤名稱 (如origin/master類的名稱)來記住 Git的master (當git pull運行git fetch ,它可能會限制您的Git的獲取。在非常舊的版本中,這甚至也禁止您的Git更新您的origin/master等。ModernGit消除了最后的無用特性。)

只要您自己不使它復雜化(通過添加refspecs或各種獲取標志),就可以完成所有操作。 合並部分仍然很棘手。 完成提取后, git pull現在運行git merge ,這會使事情變得有些復雜。

合並

合並的目標很簡單:Git首先假設您一直在工作並進行提交,而其他人也一直在工作並進行提交。 如果我們將這些提交與先前的提交朝左繪制,然后將其向右提交,給它們提供一個簡單的單字母名稱,而不是它們實際的難以理解的Gitty哈希ID,我們將得到如下所示:

          C--D--E   <-- branch (HEAD)
         /
...--A--B
         \
          F--G--H   <-- origin/branch

E是您在分支上的最新提交。 其父是提交D ; D的父母是C ; 等等。 提交H他們的最新提交,您剛剛通過git fetch引入了它。 H的父母是G ,其父母是F ,其父母是B 我們可以看到,這兩條開發線是並行發生的,從提交B的共同點( 合並基礎)開始

git merge在這種情況下所做的實際上是運行兩個git diff命令。 第一個找出您所做的更改:

git diff --find-renames <hash-of-B> <hash-of-E>

第二個差異找出了它們的變化:

git diff --find-renames <hash-of-B> <hash-of-H>

Git可以將這兩組不同的更改組合在一起 ,只要它們彼此不沖突即可。 或者說,只要Git認為它們發生沖突-Git不知道 ,它就對git diff的逐行差異做出了很多假設。 成功(也許)組合了更改之后,Git將組合的更改應用於合並基礎中的快照以生成新的快照,並與兩個父級進行合並提交

          C--D--E
         /       \
...--A--B         I   <-- branch (HEAD)
         \       /
          F--G--H   <-- origin/branch

進行新的合並提交會使分支名稱本身branch指向新的提交,就像進行新的普通提交一樣。

這是一個真正的合並:Git必須合並自合並基礎以來的更改 ,這意味着它必須首先找到合並基礎提交,然后進行兩個比較。 這是要合並 (兩組更改)的合並的動詞部分。 在完成了要合並的動詞部分后,Git進行了merge commit ,merge作為形容詞或不那么正式地稱為merge ,它使用merge作為名詞。

並非所有合並都是真實的合並

假設您運行git fetch (也許通過git pull ),但什么都沒有改變,所以您有:

...--D--E   <-- branch (HEAD), origin/branch

也就是說,您在這里有兩個不同的名稱。 您有一個名稱branch -您的分支-指向您的哈希ID為E提交(確實是一些難看的Git哈希)。 您還具有遠程跟蹤名稱origin/branch ,記住了它們在 Git的分支branch ,指向您的哈希ID為E提交,因為兩個Git中都存在相同的提交。

於是,成就了沒有工作,取得了提交,運行git fetch又過去了,只是這一次,他們已經做了一些工作,取得了一定的提交:

...--D--E   <-- branch (HEAD)
         \
          F--G   <-- origin/branch

如果現在要求Git將您的工作(或缺少工作)與他們的工作合並,Git會像往常一樣找到合並基礎,但是會發現合並基礎是提交E本身。 顯然,將提交E與自身進行比較不會發現任何變化。 無論如何,Git 可以繼續構建新的合並提交,將無更改與某些更改結合起來以得到:

...--D--E------H   <-- branch (HEAD)
         \    /
          F--G   <-- origin/branch

其中H中的快照與G中的快照完全匹配。 但是git merge的默認設置是不打擾 合並將其檢測為快進操作,並且執行合並動作, 也不進行形容詞合並提交。 不用執行新的提交H ,Git只會將分支名稱branch向前移動:

...--D--E
         \
          F--G   <-- branch (HEAD), origin/branch

並檢出提交G以使G的快照充滿索引和工作樹。

Git稱這為快速合並,但沒有合並! 快進只是對分支名稱的操作。 最終沒有任何痕跡發生,這就是為什么Git允許您禁止快速前進的非合並“合並”的原因。 但是,對於與遠程跟蹤名稱相對應的本地分支,無論如何實際上實際上是您經常想要的東西,這就是為什么這是Git的默認設置。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM