簡體   English   中英

如何使用git使本地master成為遠程分支?

[英]How to make local master a remote branch with git?

我從克隆存儲庫開始(git clone https:// blahblah_repository )。 然后,我進行了一些更改並進行了提交。 我沒有先分支,但現在我想推送到尚不存在的分支。

  1. 我如何在服務器上創建一個分支,然后假設自克隆以來,主頭尚未移動,則將我的非分支本地提交推送到新分支?

  2. 如果主控頭自克隆以來確實移動了,我該如何在服務器上分支以前的主控提交,然后將更改推送到那里?

我假設您當前正在掌握其他提交信息。

首先結帳到新分支

git checkout -b new_branch

new_branch將具有您在master分支中完成的其他提交

將新分支推送到服務器(例如github)。 如果不存在該名稱的分支,它將自動創建到服務器的新分支。

git push origin new_branch

成功推送后,立即結帳至master分支

git checkout master

現在從管理員那里刪除額外的最高提交

git reset --hard HEAD~1


如果您已經在master分支中推送了其他提交,則在重置后執行強制推送

git push origin master -f

即使使用本地主機,它也將成為遠程主機

干杯!!

對於您的問題:

1.您的意思是您在本地master分支上提交了本地更改,但是現在您想要在另一個分支上進行更改,並在git服務器上創建相同的分支。 您可以使用以下步驟:

git branch branchname
git reset --hard origin/master
git push -u origin branchname

2.對於這種情況,您希望將本地主更改重新放置在主頂部,然后將本地主更改推送到遠程。 您可以使用以下步驟:

git checkout master
git pull origin master --rebase
git push origin master

您的問題分為兩個部分:

  1. 我如何在服務器上創建一個分支,然后假設自克隆以來,主頭尚未移動,則將我的非分支本地提交推送到新分支?

  2. 如果主控頭自克隆以來確實移動了,我該如何在服務器上分支以前的主控提交,然后將更改推送到那里?

但實際上,它揭示了每個人對Git的普遍困惑,這就是分支與分支之間(或也許是分支,分支與分支)之間的區別。 Git使用一個來表示至少兩個不同的事物。 (此后,Git的分布式特性使並發症變得復雜。)

分支什么?

讓我們首先考慮問題1,並確定“分支”可能意味着的不同事物。 我們可以為其中之一使用短語分支名稱 master這樣的名稱是分支名稱 ,並且分支名稱指向一個特定的提交。

我們也還沒有真正定義提交 ,但是到目前為止,您已經非常了解提交的含義。 為了以防萬一,讓我們確定一下。 提交是一個Git對象,它可以:

  • 表示源的快照;
  • 帶有作者姓名-電子郵件時間戳,以記住誰編寫了其中的代碼; 對於可能的第二人稱的提交人來說也是一樣的,盡管如今這兩者通常是相同的。
  • 攜帶日志信息 ;
  • 標識一些提交。

最后一點至關重要,我們將在下一段中再次討論。 現在,讓我們注意每個提交都有自己的唯一哈希ID,即8e2df81...ac0ffee...badbead...或類似的哈希。 master這樣的分支名稱是這些哈希ID中的一個(並且一次只能一個,或者一次只能一個)的可讀名稱 我們稱其為branch tip ,或branchtip commit ,但是最后一句話有點讓人不滿意:如果master 分支,那么master怎么能成為分支tip名稱呢? 那么實際的分支是什么?

讓我們想象一個只有幾個提交的存儲庫。 讓我們從三個開始,而不是使用笨拙的哈希ID,讓我們使用單字母名稱來寫下它們。 我們進行的第一個提交是提交A 因為它第一次提交,所以不能有父提交。 這個無父母的提交有一個特殊的名字:叫做提交。

現在我們進行第二次提交,並將其命名為B ,將A的實際哈希ID作為其父級存儲在其中。 我們說B 指向 A 我們使C與父B並說C 指向 B 為了跟蹤所有內容,我們將名稱master指向C。讓我們一下:

A <- B <- C   <-- master

Git的所有內部箭頭都是這樣向后的-名稱master找到了最后一個提交,找到了一個較早的提交,依此類推-但是用這種方式繪制(至少在StackOverflow上的文本中)太麻煩了,所以讓我們從現在開始只使用連接線:

A--B--C   <-- master

要添加新的提交,我們從其內容(快照,作者和提交者,日志消息及其父ID C )計算其哈希ID(確實是一個大的丑陋哈希,但我們僅將其稱為D )。 我們將此提交寫入存儲庫數據庫:

A--B--C     <-- master
       \
        D

然后名稱master移至新的提交D

A--B--C     [C used to be master]
       \
        D   <-- master

通過添加新的提交(完全不接觸任何現有的提交),然后更改分支名稱以指向新的分支提示 ,分支就是這樣增長的。 因此,分支的其他必要含義,無論我們可能給它起什么名字,我都建議使用名稱“ DAGlet”。 看到“分支”到底是什么意思? - 是從分支尖端開始並向根向后工作而發現的提交的子集

這是最初令人困惑的地方,因為Git在所有這些事情上都只使用一個單詞:像ABCD這樣的DAGlet是“一個分支”,分支mastertip commit D是“一個分支”,而master本身是“分店”。 但是它們都是不同的東西。 Git只是希望我們知道我們的意思-經過一番練習,您至少在大多數時候確實知道,但是在需要時使用branch namebranch tipDAGlet這兩個詞很高興。分開他們。

創建新的分支名稱並處理不同的存儲庫

因此,現在我們可以仔細研究第一個問題的前部分:您需要在服務器上創建一個分支名稱 我們也可以同時查看第二個問題:您可能想在自己的存儲庫中創建一個分支名稱

Git是Git,它提供了兩種標准的用戶界面方法來在您自己的存儲庫中創建分支名稱 git branchgit checkout -b 我們已經看到,分支名稱僅指向一個特定的提交。 您進行了一次新的提交D而Git對該新D做出了自己的master

A--B--C
       \
        D   <-- master

我之所以在D上畫線是因為我想為在C上留的筆記留出空間。 讓我們留出空間來添加更多指向C名稱,僅就此而已,讓我們現在命名為C的新名稱old-master 我們這樣做的方法是使用git branch

git branch old-master <hash-ID-of-commit-C>

結果是:

A--B--C     <-- old-master
       \
        D   <-- master

但是請記住,這一切都是在您的存儲庫中進行的,其中包含您的分支名稱。 服務器上還有另一個Git和另一個存儲庫。 Git仍然只有ABC ,他的master仍然指向C 這就是Git變得聰明的地方: 您的 Git會為您記住您 Git擁有他的名字的地方,使用您的存儲庫專有的名稱。 這些名稱是您的遠程跟蹤分支名稱 (哦,這是另一種分支!)

遠程跟蹤分支名稱的前綴是遠程名稱,通常是origin 因此, 您的 Git為您記住了Origin origin/master ,即他們的 Git的master指向提交C 讓我們來畫一下:

A--B--C     <-- old-master, origin/master
       \
        D   <-- master

需要注意的是,我們不知道我們的Git使origin/master名字:它是對自己,通過尋找 master ,並添加origin/前面我們。 當我們將Git連接到他們的Git時,我們的Git可以從他們那里獲取任何更新,並根據需要移動origin/*名稱。 這就是遠程跟蹤分支的全部含義:它們為您記住了其他一些Git的名稱到哈希ID的映射。

還要注意,到目前為止,所有帶有名稱的東西都具有識別一個特定commit的簡單目標。 如果我們想要一個分支技巧 ,一旦找到提交,就完成了。 如果我們需要DAGlet(通過反向處理父ID找到了一些提交集),那么在這里也很不錯,因為提示提交有父ID,而那個父有另一個父ID,依此類推。 (我們可能需要告訴Git 何時停止向后退,但我將其留給其他SO發布。)

但是,現在我們需要告訴Git要求其他Git命名一個新的分支。 我們的 Git中,我們使用了git branch或(這里未顯示) git checkout -b ,但這並沒有告訴他們 Git任何東西。 除了實際登錄服務器(這將起作用!)之外,我們還需要一種方法讓Git調用其Git並要求服務器(即服務器)執行此操作。 而我們這樣做的方法是使用git push

當我們運行git push ,我們給它:

  • 遙控器的名稱,例如origin :查找URL並使用該URL調用Internet電話上的另一個Git,以及
  • 一些refspecs

簡化太多了, refspec基本上是分支名稱 ,但在它們之間用冒號配對:

git push origin master:blah

左邊名稱是我們的名稱,右邊的​​名稱blah是我們希望 Git更新,重置或創建的名稱。 我們的Git調用了自己的Git,有一個小的談話,找出犯(S),我們需要他們,那些送我們,他們不這樣做,像我們的新承諾D 1 -和要求他們建立自己的名字。 如果我們不使用:blah一部分,我們的Git使用我們的名字:“請設置您的master以同樣的方式,我有我master集,以指向這個新的提交D我給你。”


1 Git弄清楚這一點的方法是執行那些DAGlet動作。 如果我們添加了D以及EF ,那么我們的Git將從FE再到DC 當到達C ,他們的Git會說:“停下來,我有那個!” 然后我們的Git會發送DEF鏈。


但是,等等,還有更多! 還是更少?

現在,我們可以完全回答問題1,這也是問題2的完整答案! 假設我們要為服務器提供新的提交D並讓它們設置新的分支名稱test

git push origin master:test

這會向他們發送提交D ,然后要求他們設置對他們來說是新的test以指向D 他們的master可能仍然指向C ,所以他們得到了:

A--B--C     <-- master
       \
        D   <-- test

我們的git push首先傳輸DAGlet,然后要求他們設置名稱,而我們沒有要求他們設置其master 因此,假設您主人確實移動了,就像您的問題2一樣。他們有:

A--B--C--E   <-- master

首先。 我們將新的提交D發送給他們-記住,每個提交都有自己神奇的2哈希ID,因此我們的D有一個不同於其E -並要求他們設置分支名稱test ,從而為他們提供:

A--B--C--E   <-- master
       \
        D    <-- test

2魔術在於使用良好的哈希函數。 Git當前使用SHA-1,這已經足夠好了,但是現在...很好,它仍然足夠好,但是有點令人討厭


但這給我們留下了我們自己的問題

那一個git push命令完成了服務器的全部工作,但它使我們的存儲庫變得一團糟。 由於我們有origin/master我將不再使用old-master origin/master

A--B--C     <-- origin/master
       \
        D   <-- master, origin/test

我們可能不想要這樣:我們也想擁有自己的test ,以匹配剛創建的Git來記住他們的 testorigin/test test 因此,現在我們可以使用git checkout -bgit branch ,或使用一些特殊的Git魔術和簡單的舊git checkout

git checkout test

請注意,我們還沒有名為test的分支 ,或者至少還沒有。 此checkout命令即將失敗! 但是,在此之前,Git對自己說: 3 等一下,也許您打算基於origin分支創建一個新分支! 它檢查並確定現在有一個origin/test4因此它進行指向相同分支提示提交的新test ,並且副作用是,將新分支的上游設置origin/test

A--B--C     <-- origin/master
       \
        D   <-- master, test, origin/test

現在,我們只需要修復master即可指向commit C ,就像它在服務器上執行5一樣。 Git的是Git的,有多種方法可以做到一點,但一個初學者使用是git reset --hard ,這是一個有點不幸的,因為git reset --hard是有點危險的, 並且需要將第一開關回到分支master

git checkout master && git reset --hard origin/master

正如git status所說,這首先將您的Git on branch master ,然后更改當前分支(您on branch以指向與origin/master相同的提交:

A--B--C     <-- master (HEAD), origin/master
       \
        D   <-- test, origin/test

請注意,我已將此(HEAD)添加到圖形中:這是為了記住我們所在的分支。 既然有了mastertest這兩個,我們總是需要知道git commitgit reset等會移動哪一個。

我在這里完全跳過了git checkout -b ,但是它的作用是將git branchgit checkout結合在一起。 它創建一個新的分支名稱,就像git branch一樣; 然后切換到新分支,就像git checkout一樣。 如果在此過程中所有問題出在哪里,它都會神奇地設法跳過創建新分支名稱的過程。 在內部,它通過“作弊”來實現:首先執行提交級別的檢出步驟。 如果成功, 現在它將創建分支名稱並更改您所在的分支。 當您手動執行此操作時,您將被迫以更明智的順序執行操作:首先為提交提供一個名稱,然后檢查名稱,即分支提示。

您最多可以混合使用這些不同的步驟為各種其他的訂單,如果你還記得,使一個新的分支第一git checkout -b是要走的路。 但是,保持所有這些一致的關鍵是要記住,它是圖形 –由繪制提交及其父級構成的“ DAG” –是方程式的永久部分,而分支名稱(例如mastertest只是標簽,您可以隨意整理所有內容。 您需要使用名稱來查找提交,因為哈希ID太笨拙了。


3不要將計算機擬人化; 他們討厭那個。

4這假定您的Git版本為1.8.4或更高。

5還是? 如果有E承諾怎么辦? 請參閱下一節。


拿起新的提交E

回到問題2:

如果主控頭自克隆以來確實移動了,我該如何在服務器上分支以前的主控提交,然后將更改推送到那里?

我們已經看到,你不必拿起新的提交E可言,讓自己的新承諾D放到一個新的側枝。 但是您可能想看看commit E

您可以隨時通過運行git fetch來執行此操作。 Git的fetch - 不是 git pull ; 我建議避免git pull —與Git與push相反。 git push一樣,它需要一個遠程的名稱和一些可選的refspecs,但是通常您只是命名該遠程,或者讓Git為您找出它,因為反正可能只有一個遠程:

git fetch

這會調出其他的Git(使用遙控器照常URL)的網絡電話,但這個時候,而不是你自己的Git送他們的Git 你的提交,你的Git都有他們的Git發送給您提交。 和以前一樣,一旦Git到達您已經擁有的DAGlet,您的Git就會立即停止它們,因此這只會獲取新的提交(以及完成這些新提交所需的任何其他數據)。 然后,您的Git通過更新所有遠程跟蹤分支名稱來記住其分支名稱。 因此,如果它們確實具有提交E並且您運行git fetch ,則您的圖形將來自:

A--B--C     <-- origin/master
       \
        D   [whatever name(s) you have here at this point]

至:

A--B--C--E     <-- origin/master
       \
        D     [whatever]

運行git fetch 始終是安全的 ,因為git fetch僅向您的存儲庫添加新的提交 它並不─ 不能 -改變任何舊的:一個提交的哈希值ID是其內容的加密校驗,因此任何試圖改變一個新的,不同的哈希ID 任何結果。 相反,它會添加出現的所有新提交,然后更改您的origin/*名稱以匹配其他Git中的內容。

(我會注意到,順便在這里,那git push可以看出,他們的origin/master點提交E ,但它不會改變你的origin/master ,原因是git push正在發送的東西,不接受他們。這更新或創建您發送給他們他們再接受遠程跟蹤分支名稱為分支機構名稱創建有或更新,但不會更新任何其他人,這是一個設計選擇了一堆理由作出,包括Git不允許您設置名稱以指向您沒有的提交的事實,如果您還沒有其E ,則不能設置自己的origin/master來指向它。 )

暫無
暫無

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

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