我從我制作的 github 克隆了一個 repo,我做了一些更改。我在 github 上的 repo 有 2 個分支,main
并且dev
. 我想先將更改推送到 dev,然后再推送到 main。
我跑了:
git branch
在克隆(本地)存盤庫中,給我輸出:
*main
我沒有看到我在 github 上創建的 dev 分支。
如何將更改推送到 dev 分支?
uj5u.com熱心網友回復:
正如knittl 所說,git branch
顯示您的分支名稱。其他一些 Git 存盤庫,例如 GitHub 上的存盤庫,將有自己的分支名稱。
Git 中重要的實際上并不是分支名稱。這些是給你的,不是給 Git 的。更準確地說,它們的存在是為了讓 Git 可以幫助你找到你想要的提交。這是因為 Git 真正關注的是提交,而不是分支——盡管我們將提交組織到分支中——而不是檔案,盡管每個提交都包含檔案的快照。
這意味著當您使用 Git 時,您必須首先考慮提交。您需要在某種直覺層面上確切地知道提交是什么以及為您做了什么。否則 Git 所做的所有瘋狂的事情都會令人沮喪和不可能,你將生活在這個 xkcd 漫畫中。
Git 是關于提交的,那么什么是提交?它對你有什么作用?
每個 Git 提交:
有編號。每個提交都有一個唯一編號,以十六進制表示為哈希 ID(或物件 ID)。這是提交的真實名稱,沒有它 Git 無法找到提交。(為了好玩,另請參閱電視比喻。)哈希 ID 大而丑陋,人類無法記住(或者在大多數情況下,發音為??),因此 Git 有時允許我們使用縮短版本。
完全,完全,只讀:永遠凍結。那是因為數字,一旦分配,就意味著提交,它意味著在每個存盤庫中。當您的 Git 存盤庫將此提交發送到其他 Git 存盤庫時,其他 Git 將使用相同的 number。它永遠不會將該數字用于任何其他提交,甚至在您進行該提交之前也是如此。(這就是 Git 的魔力所在,如果你了解散列理論、密碼學等等,你就會知道這實際上是行不通的。總有一天,Git 會失敗。散列 ID 的絕對大小推動了那個時間是我們需要的,或者至少,我們希望它是這樣的。)
包含兩件事: Git 在您(或任何人)進行提交時所知道的所有檔案的快照,以及一些元資料,或有關提交本身的資訊,例如提交人的姓名。
每個提交的元資料中都有一堆資訊,但 Git 本身的關鍵項是每個提交都包含一個串列——通常只有一個條目長——前一個或多個提交的原始哈希 ID。Git 將這些稱為提交的父母。
每次提交中的快照都會永久凍結每個檔案,因此擁有存盤庫的任何人都可以取回每個檔案的任何版本。這些檔案以只有 Git 可以讀取的特殊格式存盤,實際上沒有任何內容可以寫入,并且它們都在提交內和提交之間進行了重復資料洗掉,因此大多數提交大多重用早期提交中的檔案這一事實意味著大多數提交占用很少的實際空間。如果你做出一個完全重用舊檔案的新提交——這可以通過多種方式實作——那么新提交實際上完全不需要檔案空間,只需要一點空間來保存它自己的元資料。
快照意味著提交使您能夠查看每個檔案在您(或任何人)提交時的狀態。但是您不能將提交的檔案用作file,因為它是一種特殊的僅 Git 格式,并且您無法按照計算機的意愿對其進行寫入。所以這意味著要使用快照,你必須讓 Git提取它。我們不會在這里討論任何細節,但這就是切換到特定提交時所做的事情git checkout
或正在做的事情:Git提取提交的檔案,就像從檔案中一樣(因為它們在檔案中)。然后,您使用或處理提取的檔案,而不是git switch
存盤在 Git 中的檔案。 這意味著當您使用檔案時,這些檔案不在 Git 中。 您最終必須進行新的提交,才能將新快照存盤到 Git 中。
同時,元資料為您提供了幾件事:
它讓您知道誰做出了提交。該
git log
命令將列印用戶的姓名和電子郵件地址,以及日期和時間戳。(--pretty=fuller
你會看到每次提交實際上有兩個;這部分是 Git 早期的“每個人都通過電子郵件發送補丁”用法的剩余部分。)它告訴你他們想告訴你他們為什么提交:這是他們的日志訊息。日志訊息的重點不是說他們做了什么——Git 可以通過比較這個提交中的快照和這個提交的父項中的快照來證明這一點——而是說明為什么它們被替換
i
為i = 2
例如。它修復了 Bug#123 嗎?是功能增強嗎?這類事情可以出現在日志訊息中。使用該父元資料,Git 可以將提交串在一起,向后。這就是歷史:隨著時間的推移,這就是這個專案中發生的事情。通過讀取最新的提交,我們找到了當前的源快照,并且通過使用它的元資料,我們找到了它之前的父提交。使用存盤的哈希 ID,Git 現在可以向您顯示父提交。該提交包含更早的祖父母提交的哈希 ID,因此 Git 現在可以向您顯示祖父母;該提交包含一個更早的提交哈希 ID,等等。
這意味著存盤庫中的提交是存盤庫中的歷史記錄。 要訪問所有歷史記錄,Git 需要最新的提交。
分支名稱可幫助您(和 Git)找到最新的提交
讓我們繪制一些提交。我們假設我們有一個很小的存盤庫,其中只有三個提交。它們將擁有三個看起來隨機、又大又丑的哈希 ID,沒人能記住或發音,所以我們將它們稱為 commits A
、B
和C
。提交C
將是最新的,因此它的元資料中將包含早期提交的實際哈希 ID B
。我們說 commitC
指向commit B
,我們這樣畫:
B <-C
ButB
是一個提交,所以它有一個父哈希 ID 的串列——只是一個又長的提交——這意味著它B
指向它的 parent A
:
A <-B <-C
A
也是一個提交,但是,作為有史以來的第一個提交,它有一個沒有父母的串列(父母的空串列)并且沒有向后指向。這就是git log
停止倒退的方法:什么都沒有了。
但是:Git 如何找到正確的哈希 ID 來提取提交C
,以便您可以首先處理/使用它?請記住,哈希 ID 看起來是隨機的。它們是不可預測的(因為除其他外,它們完全取決于您進行提交的確切秒數)。只有一種方法可以知道哈希 ID,那就是保存它:寫在某個地方。我們可以自己寫下來,然后一遍又一遍地輸入,但這一點也不好玩。所以 Git 為我們存盤它們。 這就是分支名稱。 分支名稱只存盤一個哈希 ID,即最新提交的 ID 。
如果我們有一個名為 的分支main
,那么 和C
是 的最新提交main
,則該名稱main
包含 commit 的哈希 ID C
。和以前一樣,我們說 this指向commit C
,并用箭頭繪制它:
A <-B <-C <--main
在這一點上,我喜歡變得懶惰并停止將箭頭從提交向后繪制為箭頭,因為繪圖即將發生,并且因為我沒有可用的良好“箭頭字體”。這沒關系,因為一旦我們進行提交,從該提交到其父級的向后箭頭將一直凍結,就像任何提交的所有部分一樣。它必須向后指向,因為我們不知道任何未來的哈希 ID 可能是什么,所以我們只知道它們向后指向:
A--B--C <-- main
然而,來自分支名稱的箭頭會隨著時間而變化。讓我們在 上進行一個新的提交main
,通過使用git switch main
或git checkout main
選擇 commitC
作為我們將使用的提交,然后做一些作業并運行git add
并git commit
進行新的提交D
,如下所示:
A--B--C <-- main
\
D
新的提交D
指向前一個提交C
。但是現在D
——不管它真正的哈希 ID 是什么——是最新的提交,所以名稱main
需要指向D
. 所以最后一步git commit
是 Git將D
哈希 ID 寫入名稱main
:
A--B--C
\
D <-- main
(現在我們可以再次將整個東西畫在一條線上)。
如果在 make 之前D
創建一個新的分支名稱會發生??什么dev
?讓我們畫一個,看看會發生什么:
A--B--C <-- dev, main
我們做出新的承諾D
:
A--B--C
\
D
哪個名稱會更新? Git 的答案很簡單:Git 會更新我們簽出的任何分支名稱。 所以我們需要知道,我們是現在使用名稱main
,還是現在使用名稱dev
?
為了記住我們使用的名稱,我們將特殊名稱添加HEAD
到一個分支名稱中,如下所示:
A--B--C <-- dev, main (HEAD)
這意味著我們正在使用 commit C
,但這樣做是因為 / 通過 name main
。如果我們git switch dev
或git checkout dev
,我們得到:
A--B--C <-- dev (HEAD), main
我們仍在使用 commitC
但現在我們通過name dev
使用它。如果我們D
現在做出承諾,我們會得到:
A--B--C <-- main
\
D <-- dev (HEAD)
現在有兩個最新的提交:C
是最新的main
提交,D
是最新的dev
提交。請注意,提交C
都在兩個分支上,并且D
最新dev
提交的事實不會干擾最新提交的C
事實main
。
假設我們切換回main
(如果我們愿意,可以畫dev
“上方”而不是“下方”):
D <-- dev
/
A--B--C <-- main (HEAD)
我們已經回到使用 commitC
,所以我們看到的是來自 commit 的檔案C
,而不是來自 commit的檔案D
。如果我們現在創建并切換到一個名為 的新分支br2
,我們會得到:
D <-- dev
/
A--B--C <-- br2 (HEAD), main
我們仍在使用 commitC
但現在我們通過 namebr2
這樣做。如果我們現在進行新的提交,我們會得到:
D <-- dev
/
A--B--C <-- main
\
E <-- br2 (HEAD)
這就是 Git 分支的全部意義所在。該名稱會找到最新的提交,然后我們 / Git 從那里向后作業。 當我們向后作業時,我們發現的一組提交是“在”分支上的一組提交,這就是那個分支的歷史。
根據定義,這些名稱會找到最新的提交。這允許我們通過強制其中一個分支名稱也備份一個或兩個或三個提交來“倒轉時間”。當我們使用“擦除”提交時,我們會這樣做git reset --hard HEAD~1
:它實際上并沒有消失,我們只是假裝我們從未通過確保我們無法使用分支名稱找到它來實作它。例如,如果我們正在進行br2
并做出錯誤的提交F
:
D <-- dev
/
A--B--C <-- main
\
E--F <-- br2 (HEAD)
我們可以使用git reset --hard HEAD~1
(or ) 來得到這個:git reset --hard hash-of-E
D <-- dev
/
A--B--C <-- main
\
E <-- br2 (HEAD)
\
F ??? [abandoned]
然后我們可以進行更正的提交G
:
D <-- dev
/
A--B--C <-- main
\
E--G <-- br2 (HEAD)
\
F
既然找不到 F
了,我們就再也見不到它了,就好像它不見了一樣。(Git最終可能會在 30 天或更長時間后決定我們真的不想要它,并完全放棄它,但提交很難擺脫。如果你確實將哈希 ID 保存在某處,例如在紙上或者在一個檔案中,并將其提供 git show
給例如,您可能會發現提交F
仍然存在。Git 何時甚至是否真的取消了它,特意保持了一個謎。)
多個存盤庫
Git 存盤庫主要由兩個資料庫組成:
有一個(通常更大)包含提交物件和其他支持物件。Git 將此稱為其物件資料庫或物件存盤,Git 需要哈希 ID 才能在此資料庫中查找物件。
另外,還有第二個(通常更小)名稱資料庫——分支名稱、標簽名稱和許多其他名稱——Git 使用它來查找提交和其他物件。每個名稱都包含一個哈希 ID。
我們可以將一個 Git 資料庫連接到另一個。當我們這樣做時,兩個 Git 軟體包使用哈希 ID 來交換物件。兩個存盤庫將為相同的物件使用相同的哈希 ID,因此它們可以通過比較哈希 ID 來判斷另一個存盤庫具有哪些物件。然后,一個 Git(無論哪個正在發送東西)只發送另一個需要的物件,使用它已經擁有的物件來避免發送它不需要的東西。
通過這種方式,兩個存盤庫最終共享提交。它們實際上具有具有相同哈希 ID 的相同物件,因此它們彼此共享提交。兩者都有所有內容的完整副本。
但是,此名稱資料庫中的分支名稱特定于此特定存盤庫。我們可能讓我們的 Git將它們展示給其他 Git,而其他 Git 可以獲取這些名稱和哈希 ID 并對它們進行處理,但它們是我們的分支名稱。 這個資料庫中的標簽名,我們嘗試共享:如果其他 Git 存盤庫也有標簽名,我們嘗試按原樣使用它們的名稱,并按原樣與他們共享我們的標簽名,這v1.2
意味著相同的哈希 ID 在兩個存盤庫。但是分支名稱不是以這種方式共享的!每個存盤庫都有自己的.
git fetch
然后,當您運行or時,您不是共享分支名稱,而是git fetch origin
告訴您的 Git:呼叫他們的 Git 軟體,讓它連接到他們的存盤庫,并通過他們的分支分支名稱找到他們所有的最新提交。然后帶來所有的提交。獲取他們所有的分支名稱并將其更改為我的遠程跟蹤名稱。 他們main
成為你的origin/main
;他們dev
成為你的origin/dev
;等等。這樣,無論他們是否向其分支添加了提交,您的分支都不會受到干擾。您會獲得他們的任何新提交,并且您的遠程跟蹤名稱會記住他們最新的提交哈希 ID。
但這對于git push
.
“非快進”錯誤來自git push
當你運行git push origin
orgit push origin dev
時,你讓你的 Git 呼叫他們的 Git 軟體和存盤庫,就像你做的一樣git fetch
,但是這次你選擇讓你的Git 將你的新提交發送給他們。與其讓你的 Git 讀取他們的分支名稱并找到他們的新提交,不如讓你的 Git發送哈希 ID 并找到你的新提交。然后你的 Git 將這些新的提交物件交給他們……然后要求或命令他們設定他們的分支名稱之一。他們沒有適合您的遠程跟蹤名稱!您只需要求他們直接設定他們的分支名稱。
假設你有:
D <-- dev (HEAD)
/
A--B--C <-- main
在您的存盤庫中,但他們E
在其存盤庫中獲得了一些提交,如下所示:
A--B--C <-- main [their main, in their repository]
\
E <-- dev [their dev, in their repository]
你的 Git 發送你的新提交D
并要求他們設定dev
記住提交D
。
如果他們這樣做了——他們不會——他們將如何找到他們的承諾E
?請記住,他們的Git 將使用他們的分支名稱來查找最新的提交。如果他們dev
找到 commit 的動作D
,D
但沒有導致——而且沒有——他們將E
“丟失”他們的commit E
。
如果發生這種情況,他們會說:不,我不會將 my 設定為最新提交,dev
因為D
那樣會丟失一些其他最新提交。 這顯示在您的git push
as:
! [rejected] dev -> dev (non-fast-forward)
error: failed to push some refs to ...
hint: Updates were rejected because a pushed branch tip is behind its remote
hint: counterpart. Check out this branch and integrate the remote changes
hint: (e.g. 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
發生這種情況時,您需要:
- 運行以獲取您缺少
git fetch
的任何新提交; - 檢查您和他們的提交以及這些提交可能相互之間的關系(父、子、兄弟等):
git log
與各種選項一起使用;查看漂亮的 Git 分支圖 - 如果有必要,可以修改你的提交,例如,使用
git rebase
; - 重復你的
git push
現在你已經糾正了一些事情,或者git push --force-with-lease
如果你確定你應該告訴他們的 Git 存盤庫是的,請丟失這些提交!
這是很多東西要知道。但是,如果您要使用 Git 和分布式存盤庫,則需要了解它。這一切至少應該是隱約熟悉的,提交的概念以及它們為您做什么應該非常熟悉。
uj5u.com熱心網友回復:
你可以試試這個:
git fetch
git checkout dev
git add .
git commit -m "your commit message here"
git push
git fetch
將從您的遠程倉庫獲取所有現有分支的更新到您的本地。然后,您可以git checkout dev
切換到dev
分支。然后最后commit
和push
dev
然后推送到main
,你可以做一個pull request
,批準它,然后合并到main
。
uj5u.com熱心網友回復:
git branch
只顯示本地分支,顯示遠程分支使用git branch -r
(顯示本地和遠程分支,使用git branch -a
)。
通過指定源(本地)和目標(遠程)分支,您可以將任何本地分支推送到任何遠程分支:
git push origin main:dev
會將本地主分支上的提交推送到存盤庫“origin”中的遠程開發分支。
uj5u.com熱心網友回復:
顯然,
git branch dev
git checkout dev
git pull origin dev
git push origin dev:dev
作業。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/470882.html