[英]How to push local git repository to existing project on remote Github project and after that add another branch to other developer work with?
[英]How to push a local repository to a branch of another repository on Github
是否可以將僅存儲在我的本地計算機上的項目添加到 Github 上現有項目的分支中。
例如添加:
local-project
到 Github 回購:
https://github.com/myusername/my-project/local-branch.git
可以這樣做還是應該首先將本地存儲庫推送到它自己的 Github 存儲庫?
附加信息:根據我目前收到的評論,這是一些額外信息。 所以基本上我有一個 Github 存儲庫(我的項目),其中包含一個當前正在生產的 Next.js 項目。 我想將此項目更新為一種完全不同的語言(REACT NATIVE),該語言目前仍在開發中。 我一直在使用的 React Native 版本僅在我的計算機上本地,因為我克隆了一個 repo 並且尚未將其推送到 Github。 我的最終目標是將 React Native 版本作為(my-project)Github repo 中的一個分支(local-branch)。 然后當本地分支准備好生產時,我可以簡單地將它合並到主分支。
首先:您可以推送分支,而不是存儲庫。
現在:
git remote add remote-repo <remote-repo-url>
git push remote-repo local-branch:<remote-new-branch-name>
首先要澄清一些術語,您不能“推送本地存儲庫”。 相反,您可以將分支從本地倉庫推送到遠程倉庫。 首先,您需要創建一個遙控器:
git remote add <name> <url>
例如
git remote add my-remote https://github.com/myusername/my-project/local-branch.git
默認情況下, git clone
另一個 repo 時,git 將創建一個遠程命名origin
。
一旦你有了遙控器,你就可以推送你的分支:
git push my-remote my-branch
雖然您可以對任何分支和任何存儲庫執行此操作,但通常只有兩個存儲庫具有共同的歷史記錄時才有意義,例如當您在本地克隆 github 存儲庫時,或者如果您在本地創建新存儲庫並希望將分支推送到github 上的新空倉庫。
您需要從明確的定義開始。 尤其是:
Git 存儲庫的核心是一個提交集合——一個包含 Git 所稱的提交對象的數據庫,以及使提交真正有用的支持對象(使它們包含文件)——以及一個輔助數據庫來幫助您和Z0BCC50105ADE247B760提交。
這反過來意味着 Git 完全是關於commits 。 這與文件無關! 提交包含文件,這就是我們使用提交的原因。 但是 Git 並不完全關心這些文件,至少在高層次查看時是這樣。 而且,它也不是關於分支的,但我們組織並——最重要的是——通過分支名稱找到提交,這是分支進入圖片的方式。
Git 並沒有真正的項目概念。 這個概念由你來定義。 Git 有存儲庫的概念,存儲庫由提交和其他對象組成。 因此,如果您有一個現有的存儲庫,那么您就有一堆現有的提交(以及它們的其他對象)。
在您的更新中,您說:
我想將此項目更新為一種完全不同的語言(REACT NATIVE),該語言目前仍在開發中。
你還在談論一個“項目”。 Git 沒有這些,所以你必須 map 你自己的“項目”概念到 Git 擁有的東西上:包含提交的存儲庫。
我一直在使用的 React Native 版本僅在我的計算機上本地,因為我克隆了一個存儲庫 [sitory]...
在這里,您提到了另一個——顯然不同的——存儲庫。 這是 Git 明白的。 您現在似乎至少有三個存儲庫:
並且尚未將其推送到 Github。
這里的代詞“它”似乎是指您的第三個存儲庫。 但是你不能——不能——推送一個存儲庫。 您推送一些提交(及其支持對象),它們存在於存儲庫中,您和 Git 使用分支名稱和其他名稱找到它們。
現在是時候多談談存儲庫、提交和克隆了。
數據庫采用許多 forms,但構成任何 Git 存儲庫的大部分都是簡單的鍵值存儲。 鍵值數據庫(在 Wikipedia 鏈接中查看更多信息)將key作為其輸入,並使用它來檢索value 。
對於提交 object,鍵是提交的名稱。 提交的“名稱”是一個大的、丑陋的、隨機的(但實際上不是隨機的) hash ID ,例如4af7188bc97f70277d0f10d56d5373022b1fa385
。 這些 hash ID 特別神奇,因為:
這種深奧的魔法以各種方式與密碼學相關,而 Git [ab] 使用它的方式實際上不能永遠有效。 hash ID 空間的絕對大小是為了確保它的工作時間足夠長,以至於沒人關心。 但您真正需要知道的是 hash ID(Git 正式稱為object ID或 OID)是提交的“真實名稱”。 Git 確實需要這個 hash ID 才能在其所有對象的大數據庫中找到提交。
如果這是 Git 擁有的唯一數據庫,我們都必須記住這些看似隨機的 hash ID。 這將是非常糟糕的,因為人類不擅長這些事情。 所以 Git 有第二個數據庫。 第二個數據庫也是一個簡單的鍵值對存儲,但其中的鍵是諸如分支和標簽名稱之類的東西,它們是人類可讀的並且對人類有意義:諸如main
或master
、 develop
等名稱。 Git 將在第二個數據庫中的分支名稱下的某個分支中存儲最新提交的 hash ID。
這意味着您只需要記住您正在使用的分支名稱。 你說“給我最新的main
提交”或“給我最新的dev
提交”,Git 從名稱數據庫中找出 hash ID,然后使用它從大型全對象數據庫中找出提交。
換句話說,您不需要記住 hash ID。 您仍然需要知道提交是編號的,使用這些有趣的奇怪的十六進制OID 或 hash ID 事物,並且每個提交都有一個。 但是您不必記住其中任何一個:例如,只需運行git log
, Git 本身就會顯示給您,您可以使用鼠標剪切和粘貼 Z0800FC577294C34E0B28AD245 ,你會時不時地需要一個——可能很少,可能一周一次或一天兩次或其他什么,但總有一天,可能)。
除了像這樣編號之外,還有一個提交:
完全只讀。 這是編號系統正常工作所必需的,但這意味着一旦提交,您實際上無法更改任何有關提交的內容。 這並不是什么大問題,因為 Git 中的提交通常幾乎不會占用任何空間。
存儲兩個東西:
每個提交都有每個文件的完整快照。
每個提交都有一些元數據,或者關於提交本身的信息。
第一個子要點似乎與提交通常很小的說法相矛盾。 如果每個提交都包含每個文件,存儲庫數據庫不會膨脹嗎? 它會,除了一些非常聰明的 Git 技巧。 第一個技巧是提交中的文件以一種特殊的、只讀的、僅限 Git 的格式存儲(實際上作為那個大型全對象數據庫中的對象),其中重復的文件內容被刪除了重復。
由於大多數提交主要重復使用之前提交的大部分文件,並且這些文件會自動重復數據刪除,因此它們不占用空間! 只有更改的文件實際上占用了任何空間。 Git 稍后(不是馬上)也壓縮這些; 只要它們是普通文本或編程語言內容,這通常會非常有效。 (不過,對於二進制文件,它通常會失敗,這就是為什么 Git 不適合存儲大多數二進制文件的原因。)
因此,作為 Git 對象的壓縮和去重文件是Git在每次提交中存儲每個文件的方式,而不是在每次提交中實際存儲每個文件,因此不會讓存儲庫變得非常胖。 你不需要知道這一點來使用 Git,你只需要知道每個文件似乎都被永久存儲在每次提交中。 也就是說,進行一次提交,你就可以永遠取回所有文件——或者更確切地說,只要你能找到那個提交。 您將需要它的 hash ID,(您知道為什么 hash ID 很重要嗎?現在?)
但是我剛才說你不需要記住 hash ID,這是真的。 那么這是如何工作的呢? 好吧,讓我們更仔細地看看那個元數據。 每個提交都存儲有關自身的信息,這些信息包括提交人的姓名和 email 地址,以及一些日期和時間戳等。 但是這里有一個關於Git 本身的重要信息:每個提交都存儲了之前提交的 hash ID。
更准確地說,每個提交都有一個先前提交 hash ID 的列表。 但是這個列表通常只有一個元素長。 那一個元素,一個列表條目,保存父提交的hash ID。 這意味着提交具有父/子關系,大多數提交只有一個父(母親?父親?在這里選擇你喜歡的任何東西,Git 與性別無關)。
因為提交是完全只讀的,所以子提交可以記住其父提交的“名稱”(哈希 ID),因為當我們創建子提交時父提交存在。 但是父母不記得它的孩子的名字,因為它的孩子——如果有的話——還不存在。 孩子一出生,就被低溫冷凍,無法得知未來孩子的名字。
我們說子節點指向它的父節點,如果我們願意,我們可以通過這種方式繪制一些提交。 使用單個大寫字母代表真實的 hash ID,我們將調用分支H
(代表 Hash)中的最后一個提交,並將其繪制如下:
<-H
從H
伸出的那個箭頭是H
如何在其元數據中使用其父 hash ID 存儲來指向其父。 我們現在將其父級繪制為字母G
,因為它位於H
之前:
<-G <-H
當然G
指向其父級:
... <-F <-G <-H
正如你所看到的,這形成了一個無限的向后指向鏈,除了當我們回到有史以來的第一次提交時,歷史最終會耗盡。 第一次提交沒有父母,就像某種處女出生但甚至沒有母親一樣,所以那個提交——我們稱之為A
畢竟並沒有向后指向:
A--B--...--G--H
我們可以變得懶惰並停止將箭頭繪制為箭頭,因為我們知道它們是提交的一部分並且無法更改,因此必須向后指向。
上面的問題是你仍然要記住 hash ID H
,鏈中的最后一次提交:鏈的尖端。 不過,我們已經看到 Git 存儲庫包含第二個名稱數據庫,並且分支名稱包含上次提交的 hash ID。 與指向較早提交的提交一樣,我們說分支名稱指向提示提交,我們將其繪制如下:
...--G--H <-- branch
要將新提交添加到分支,Git 將簡單地寫出新提交及其完整快照和元數據,在此過程中獲取新的唯一 hash ID。 hash ID 看起來是隨機的; 我們在這里將其稱為I
,即H
之后的下一個字母。 提交I
將指向提交H
,任何提交都必須向后指向其父級:
...--G--H <-- branch
\
I
並且因為新提交I
是提交鏈的新尖端,所以 Git 現在會將I
的 hash ID 寫入數據庫中的分支名稱,因此名稱現在指向I
:
...--G--H branch
\ ↙︎
I
(我在這里使用的箭頭有點蹩腳;這就是為什么我對提交之間的箭頭變得懶惰)。 畢竟,我們不需要I
在單獨的行上:將其繪制為更有意義:
...--G--H--I <-- branch
請注意,從分支名稱中伸出的箭頭可以並且確實一直在移動。 這使得它與從提交中伸出、指向提交的父級的剛性、向后指向的箭頭非常不同。 Git 簡單地將分支名稱定義為“這是最后一次提交”。 也就是說,無論 hash ID 在分支名稱中,這是分支上的最后一次提交。 因此,要更改哪個提交是最后一次提交,您需要將 Git 將新的 hash ID 填充到分支名稱中。 這不僅可以讓您向分支添加提交,還可以讓您從分支中刪除提交:
H--I ???
/
...--G <-- branch
如果我們在分支名稱中有 Git 存儲G
的 hash ID,我們已經從分支中刪除了提交H
和I
它們仍在存儲庫中,但現在您需要知道它們的 hash ID! 如果您不知道提交I
的 hash ID,您將永遠找不到它。 Git 可以向后工作——給定一個指向提交I
的名稱branch
, Git可以為您遵循提交到父級的箭頭; 這就是git log
的工作原理,但 Git無法向前工作。 沒有前向箭頭!
除了上述之外,Git 還提供克隆存儲庫的能力。 以下是克隆的工作原理:
git clone
並給它一個 URL。main
或master
成為您的origin/main
或origin/master
; 他們的develop
,如果他們有,成為你的origin/develop
; 等等。你最終會得到你的兩個數據庫充滿了東西:
origin/
開頭的遠程跟蹤名稱,而不是分支名稱。 這些是你現在如何找到提交的方法。 作為最后一步,您的 Git 現在創建一個您自己的分支名稱。 例如,如果您告訴git clone
到git clone -b develop
,您的 Git 將創建您自己的分支名稱develop
。 如果您沒有使用-b
選項(大多數人不使用),您的 Git 會詢問他們的 Git他們推薦什么名稱,並創建該名稱。
在任何一種情況下,您的新分支所具有的提交,作為它的提示提交,都是相同的提交 hash ID 在他們的名字中,在他們這邊拼寫相同的方式。 也就是說,如果您讓您的 Git 創建main
因為他們推薦main
,那么您的main
作為其最后一次提交的提交與您的origin/main
選擇的提交相同,這與您運行git clone
時他們的main
選擇的提交相同git clone
。
到目前為止——可能是seconds ,這在計算機上是很多時間——它們的分支名稱可能是 select 不同的提交。 但是在您運行git clone
時,它們的分支名稱選擇了特定的提交。 您的Git 使用您的存儲庫的遠程跟蹤origin/*
名稱記住所有這些。 您的存儲庫有您自己的分支名稱,您可以隨意創建和更新它們,它們與它們的分支名稱不同,即使您使用相同的拼寫。 那是因為他們的數據庫不是您的數據庫。
你和他們共享的是commits 。 Those are read-only—neither of you can change them—and have hash IDs that every Git in the universe agrees are the right hash IDs, via the hash ID magic. 因此,您對原始( origin
)存儲庫的克隆與該原始存儲庫非常密切相關。 您有自己的分支名稱,但您共享提交。
給定任意兩個存儲庫——通常是相關的,盡管一開始這實際上並不是必需的——我們可以將提交從一個存儲庫轉移到另一個存儲庫:
git fetch
。git push
。 在這兩種情況下,我們都必須給我們的 Git(我們的軟件與我們的存儲庫一起運行)提供另一個 Git(他們的軟件和他們的存儲庫)的 URL。 如果我們有一個通過克隆創建的密切相關的存儲庫,我們已經有了 URL,因為我們的 Git 將它保存在名稱origin
下。 所以我們只運行:
git fetch origin
我們的 Git 調用他們的 Git。 They list out their names and hash IDs, and our Git can tell whether we already have some commits—because hash IDs are the true names of objects, and the commits have unique hash IDs—or whether we need them. 如果我們需要它們,我們的 Git 會要求它們,它會自動要求發件人發送父 hash ID,以便我們可以查看是否也有這些 ID。 通過這種方式,我們的 Git 可以從他們那里獲得所有我們沒有的提交。 我們不要求我們已經擁有的任何提交。 Git 使用這些信息不僅可以確定哪些提交對我們來說是新的,還可以確定這些提交中的哪些文件是新的,然后只向我們發送新內容。
我們的 Git 然后將所有新提交和支持對象粘貼到我們的對象數據庫中。 我們現在擁有了他們所有的提交,以及我們從未提供給他們的任何提交。 Then our Git updates our remote-tracking names because we know what hash IDs their branch names held at the time we ran git fetch
and we have all those commits, so our Git can update our memory of their branch names.
In fact, this git fetch
is how the main part of git clone
works: all git clone
does is create the empty repository, stash the URL away under the name origin
, run git fetch
, and run one final operation to create and check out a分店名稱。 因此,如果您使用git fetch
連接兩個不相關的存儲庫,則一旦完成提取,這兩個存儲庫現在是相關的。
git push
命令與 Git 接近git fetch
的相反。 ( Remember this: git push
's opposite is git fetch
, not git pull
. This was a mistake in naming that Git made early on and we're just stuck with it now. You just have to memorize it: push/fetch, not推/拉。)雖然有一些很大的區別:
使用git fetch
,操作是“獲取提交並更新遠程跟蹤名稱”。 默認值為所有新提交和所有遠程跟蹤名稱。
使用git push
,操作是發送新的提交。 我們必須選擇我們一方將從哪個提交開始,為他們提供新的提交。 所以我們在這里運行git push origin somebranch
,向他們提供我們發現的分支名稱為somebranch
的任何新提交。 我們將首先為他們提供一個提示提交。 他們可能沒有它,所以他們會說“哦,是的,發送那個”,這意味着我們必須提供它的父母。 如果他們沒有,他們也會要求,這意味着我們提供祖父母,等等。 最終我們回到他們已經擁有的一些提交——因為我們通過克隆從他們那里得到了它——或者我們 go 一直追溯到我們的歷史並得到我們第一次提交,它沒有父提交,所以我們說“就是這樣有”。
一旦我們知道哪些提交對他們來說是新的,我們的 Git 會使用它的智能來 package 只添加那些提交和對他們來說也是新的文件,然后我們發送這些東西,然后他們將其粘貼到存儲庫的對象中數據庫。 但現在我們,或者他們,有一點進退兩難的境地。 他們將如何找到這些對象? 他們需要一個名字。
當我們使用git fetch
時,我們創建或更新了一個遠程跟蹤名稱以記住其分支名稱之一的提示提交。 使用git push
,他們沒有我們的遠程跟蹤名稱。 相反,我們要求他們(默認情況下禮貌地)看看他們是否可以,請,創建或更新他們的一個分支名稱以記住我們最近的提交。
如果somebranch
是他們的新名稱,他們可以創建該名稱。 這不會打擾他們現有的任何名字。 所以這很容易,他們只需說“好”,就去做,我們都很好。 但是如果somebranch
是他們的分支名稱之一,他們現在要做的就是檢查以確保我們只是向他們的分支添加提交。
讓我們 go 回到我們之前的插圖。 假設我們有這些名稱:
...--G--H <-- main
\
I <-- develop
讓我們進一步假設我們有幾秒鍾、幾小時或幾天前從origin
站獲得的遠程跟蹤名稱:
...--G--H <-- main, origin/main
\
I <-- develop, origin/develop
我們現在使用git switch develop
來選擇名稱develop
作為我們當前的分支名稱:
...--G--H <-- main, origin/main
\
I <-- develop (HEAD), origin/develop
這個技巧,將特殊名稱HEAD
添加到繪圖並將其“附加”到其中一個分支名稱,是我們如何顯示我們在自己的 Git 存儲庫中使用的分支名稱。
現在讓我們以通常的方式在我們的 Git 存儲庫中進行新的提交(編輯文件, git add
, git commit
,編寫提交消息等)。 我們得到這個:
...--G--H <-- main, origin/main
\
I <-- origin/develop
\
J <-- develop (HEAD)
我們現在運行git push origin develop
。 我們會給他們提交J
,他們會說:嗯,新的 hash ID,好的,發給我J
。 接下來我們將向他們提供提交I
,因為那是J
的父母。 他們會說不,謝謝,已經有了。 我們現在知道所有或幾乎所有的提交! 他們有I
,但這意味着他們有H
,這意味着他們有G
,依此類推,一直回到第一個提交!
因此,我們的 Git 將 package 向上提交J
和任何不與H
中的文件重復的新到J
文件(也許我們將文件恢復到以前的方式)。 我們會將它發送過來,然后我們會禮貌地問他們:如果可以,請將您的 name develop
設置為指向提交J
(當然是提交J
的真實 hash ID)。
如果他們的develop
——正如我們的 memory 所代表的, origin/develop
——仍然指向提交I
,提交J
添加到他們的develop
。 所以他們會說OK,完成了我們的禮貌請求,我們會知道他們的develop
now 名稱 commit J
,我們的 Git 將像這樣更新我們的圖片:
...--G--H <-- main, origin/main
\
I--J <-- develop (HEAD), origin/develop
但是假設在這幾秒鍾、幾小時或幾天內,其他人在他們的develop
中添加了一些其他新的提交K
也就是說,他們有:
...--G--H <-- main
\
I--K <-- develop
在他們的存儲庫中(這些是他們的名字,所以我們不必知道或關心他們的HEAD
在哪里,而且他們還沒有J
)。
我們向他們發送一個新的提交J
,他們將其保存在他們的數據庫中:
...--G--H <-- main
\
I--K <-- develop
\
J
然后我們禮貌地請他們請 - 如果可以 - 將他們的develop
移動到J
。 但是這次不行! 如果他們這樣做,他們將“丟失”他們的提交K
。 所以他們會說:不,如果我這樣做,我會失去一些東西。 (Git 稱之為“非快進”,這是 Git 通常難以理解的方式。)我們會得到一個錯誤:
! [rejected] develop -> develop (non-fast-forward)
對於這個錯誤,我們需要做的通常是運行git fetch
,這將使我們提交K
,所以我們現在有:
...--G--H <-- main, origin/main
\
I--K <-- origin/develop
\
J <-- develop (HEAD)
一旦我們有了它,我們就可以決定:
K
好嗎? 或者,我們應該要求他們把它扔掉嗎?K
是好的,我們想對此做什么? “我們想要做的”的主要選擇是使用git rebase
或git merge
。 運行git fetch
然后想運行git rebase
或git merge
是很常見的。 這就是git pull
存在的原因:它運行git fetch
,然后運行git rebase
或git merge
git pull
的缺點很多:
K
。 我們只是假設它是好的。K
git merge
和git rebase
是什么!git merge
或git rebase
無法完成時,我們不知道該怎么辦,這種情況經常發生,很重要。git merge
或git rebase
中的哪一個! 我們不知道下一步該做什么,因為我們不知道發生了什么。 因此,如果您是 Git 的新手,請不要使用git pull
(還)。 稍后,您可能想使用它,一旦您牢記所有這些內容。 我自己仍然大多不使用它,而且我已經使用 Git 將近二十年了。 我很不喜歡git pull
。 (在過去一年左右的時間里,它發展了一種新模式,可以滿足我的需求,但我仍然寧願只運行兩個命令。)
在您擔心是否應該將另一個項目推入存儲庫之前(您當然可以,但也許您不應該,這是一個判斷問題),了解存儲庫為您做了什么。 決定是否要將 map“項目”到“存儲庫”一對一、多對多、多對一、一對多或其他。 “monorepo”(一個存儲庫中的所有內容)和“polyrepo”(一個或多個項目的多個存儲庫)方法各有利弊。 你不會總是第一次就搞定這一切,但請注意,這就是你在這里所做的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.