.git
.web
.xcode
.服務器
.ssh
最近須要將不一樣的客戶的代碼分開管理,因此須要爲這些代碼分別建立分支。工具
目前版本庫中分支結構以下:post
[yuhuashi@local:Project]$ git branch -a
* master
remotes/origin/HEAD -> origin/master
remotes/origin/masger
remotes/origin/master網站
其中 master 分支是客戶 A 所使用的分支。ui
其它客戶則以 masger 分支爲基礎版本建立。this
大體需求的流程以下:
1.以 masger 分支做爲基礎爲客戶 B 建立分支 join。
2.將客戶 A 的 master 分支重命名爲 work。
修改後的分支應該以下所示:
$ git branch -a
* join
remotes/origin/HEAD -> origin/join
remotes/origin/work
remotes/origin/join
remotes/origin/masger
好了,敘述完了需求,那麼就從頭開始作起。
1.從遠程倉庫 clone 代碼到本地,並建立本地倉庫。
[yuhuashi@local:~]$ git clone ssh://user@192.168.4.9/~/proj/Project Cloning into 'Project'... user@192.168.4.9's password: remote: Counting objects: 7981, done. remote: Compressing objects: 100% (5962/5962), done. remote: Total 7981 (delta 2504), reused 6801 (delta 1784) Receiving objects: 100% (7981/7981), 125.27 MiB | 25.29 MiB/s, done. Resolving deltas: 100% (2504/2504), done. [yuhuashi@local:~]$ cd Project/ [yuhuashi@local:Project]$ git branch -a * master remotes/origin/HEAD -> origin/master remotes/origin/masger remotes/origin/master [yuhuashi@local:Project]$
解釋:經過 git branch -a 命令能夠看到當前的分支結構。
2.建立一個空分支
[yuhuashi@local:Project]$ git checkout --orphan join Switched to a new branch 'join' [yuhuashi@local:Project]$ git rm -rf . rm '.gitignore' rm 'Project.xcodeproj/project.pbxproj' rm 'Project/Base.lproj/LaunchScreen.xib' rm 'Project/Base.lproj/Main.storyboard' ...... [yuhuashi@local:Project]$
[yuhuashi@local:Project]$ git branch -a master remotes/origin/HEAD -> origin/master remotes/origin/masger remotes/origin/master [yuhuashi@local:Project]$
解釋:git checkout 的時候指定 --orphan 參數能夠建立一個不包含歷史 log 的分支,若是使用 git branch join 會建立一個與當前分支如出一轍的叫作 join 的分支,會帶着 master 分支的 log。
分支建立好以後再使用 git rm -rf . 來刪除當前分支下全部被跟蹤的文件。
固然,使用 checkout 建立的這個空分支使用 git branch -a 命令是看不見的,必須 commit 一次才能看見。
可是直接在這個空分支中 commit 也是不能夠的(以下所示),必須在這裏作一些修改才能提交。
[yuhuashi@local:Project]$ git add . [yuhuashi@local:Project]$ git status # On branch join # # Initial commit # nothing to commit (create/copy files and use "git add" to track) [yuhuashi@local:Project]$ git commit -m "Initial commit" # On branch join # # Initial commit # nothing to commit (create/copy files and use "git add" to track) [yuhuashi@local:Project]$
既然 join 分支是爲了客戶 B 建立的,那麼咱們就把客戶 B 的工程拷貝到當前文件夾中。
[yuhuashi@local:~]$ cd .. [yuhuashi@local:Project]$ git clone ssh://user@192.168.4.9/~/proj/Project b_join Cloning into 'Project'... user@192.168.4.9's password: remote: Counting objects: 8001, done. remote: Compressing objects: 100% (5722/5722), done. remote: Total 8001 (delta 2517), reused 7309 (delta 2042) Receiving objects: 100% (8001/8001), 125.28 MiB | 29.03 MiB/s, done. Resolving deltas: 100% (2517/2517), done. [yuhuashi@local:~]$ cd b_join [yuhuashi@local:b_join]$ git checkout -b join origin/masger Branch join set up to track remote branch join from origin. Switched to a new branch 'join' [yuhuashi@local:b_join]$
咱們隨便找一個目錄,而後把工程從新 clone 下來,這樣作的目的是先把客戶 B 的代碼下載下來,以便拷貝到咱們剛剛爲客戶 B 建立的 join 分支中。
[yuhuashi@local:b_join]$ cp -r ./* ../Project/ [yuhuashi@local:b_join]$ cd ../Project
複製的時候要注意,來源路徑要寫成「目錄名/*」,就像上面的栗子中寫成了 ./*,這樣不會把 ~/b_join/.git 文件夾拷貝到 ~/Project/ 目錄下,覆蓋掉咱們的 .git 目錄。
如今咱們新建的 join 分支下已經有客戶 B 的代碼了,那麼就能夠提交這個分支了。
[yuhuashi@local:Project]$ git status # On branch join # # Initial commit # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # Project.xcodeproj/ # Project/ # ProjectTests/ nothing added to commit but untracked files present (use "git add" to track) [yuhuashi@local:Project]$ git add . [yuhuashi@local:Project]$ git commit -m "Initial commit" [join (root-commit) cf9b8d6] Initial commit 6771 files changed, 1420915 insertions(+) create mode 100644 Project.xcodeproj/project.pbxproj create mode 100644 Project/Base.lproj/LaunchScreen.xib create mode 100644 Project/Base.lproj/Main.storyboard ...... [yuhuashi@local:Project]$ git branch -a * join master remotes/origin/HEAD -> origin/master remotes/origin/masger remotes/origin/master [yuhuashi@local:Project]$
git commit 以後再使用 git branch -a 就能夠看到咱們建立的 join 分支了,並且使用 git log 命令查看會發現並無帶有 master 分支的歷史記錄。
3.建立遠程分支
此時這個 join 分支還只是在咱們的本地庫中,咱們得把它推送到遠程庫上去,這樣別人才能使用它。
[yuhuashi@local:Project]$ git push origin HEAD:join user@192.168.4.9's password: Counting objects: 6866, done. Delta compression using up to 8 threads. Compressing objects: 100% (4910/4910), done. Writing objects: 100% (6866/6866), 119.69 MiB | 14.84 MiB/s, done. Total 6866 (delta 1751), reused 6811 (delta 1743) To ssh://user@192.168.4.9/~/proj/Project * [new branch] HEAD -> join [11:12:30 yuhuashi@local:Project]$ git branch -a * join master remotes/origin/HEAD -> origin/master remotes/origin/join remotes/origin/masger remotes/origin/master [yuhuashi@local:Project]$
其實建立遠程分支就是把本地分支推送到遠程便可,也就是執行 git push origin HEAD:join,這樣本地的 join 分支就被推送到 origin/join 了。
好了,到了這一步,咱們爲客戶 B 建立乾淨分支的目的就完成了,接下來爲客戶 A 把 master 分支重命名爲 work。
4.重命名遠程分支
重命名遠程分支就是先刪除遠程分支,而後重命名本地分支,最後再將本地分支推送到遠程庫。
通常的作法以下:
刪除遠程分支。這種方式是推送一個空分支到遠程庫,其實就是刪除遠程分支:
git push origin :masger
重命名本地分支:
git branch -m masger develop
將本地分支推送到遠程庫:
git push origin HEAD:develop
可是LZ這種狀況不太同樣,由於要重命名的是 master 分支,master 分支是默認分支,而且別人在訪問咱們的庫的時候默認顯示的是這個分支的內容,因此咱們不能直接刪除,不然會收到一個異常:
$> git push origin :master
remote: error: By default, deleting the current branch is denied, because the next remote: error: 'git clone' won't result in any file checked out, causing confusion. remote: error: remote: error: You can set 'receive.denyDeleteCurrent' configuration variable to remote: error: 'warn' or 'ignore' in the remote repository to allow deleting the remote: error: current branch, with or without a warning message. remote: error: remote: error: To squelch this message, you can set it to 'refuse'. remote: error: refusing to delete the current branch: refs/heads/gitcafe-pages To git@gitcafe.com:ranmocy/ranmocy.git ! [remote rejected] gitcafe-pages (deletion of the current branch prohibited) error: failed to push some refs to 'git@gitcafe.com:ranmocy/ranmocy.git'
$>
解決辦法就是修改默認分支爲其它分支。可是應該怎麼修改遠程庫的默認分支呢?咱們在本地操做的時候修改的都是本地分支,沒法影響到遠程,並且搜索出來的文章裏都是用了相似 git@osc 這樣的代碼託管網站,能夠直接在 web 頁面上修改遠程默認分支,以下圖所示:
那麼咱們本身的遠程倉庫應該怎樣修改默認分支呢?
其實對於你本地的庫來講,被你 clone 的那個庫就是遠程庫,而若是你在遠程庫裏直接操做,那不就至關因而修改遠程庫的本地分支了嗎?
因而你得先登陸到遠程庫的服務器上,而後在裏面修改它的本地分支。
# 登陸到遠程 git 庫所在的服務器 [yuhuashi@local:Project]$ ssh user@192.168.4.9 # 進入遠程 git 庫所在的目錄 [yuhuashi@local:Project]$ cd proj/Project # 能夠看到,咱們的 join 分支已經建立成功了,而且當前是在 master 分支上。並且能夠看出來咱們前面的猜想沒有錯,在本地庫看到的遠程分支,其實就是遠程庫的本地分支。 [yuhuashi@local:Project]$ git branch -a join masger * master # 那麼就把遠程庫的本地分支切換成其它分支吧。哎,爲什麼說這個操做只能在一個工做樹中執行呢? [yuhuashi@local:Project]$ git checkout join fatal: This operation must be run in a work tree # 原來這個遠程庫只有 git-dir,沒有 wrok-tree。 # git-dir 就是 git 進行版本控制的工做空間,存放一些只有 git 纔看得懂的文件,通常是 .git 文件夾,只不過 LZ 這個文件夾的名字不叫作 .git,而是與工程同名。 # work-tree 就是咱們平時工做使用的目錄,也就是被 git 所控制的目錄,也就是某一個分支中的內容,裏面是咱們本身保存在 git 上的文件。通常是 .git 文件夾的上一級目錄。 # git pull、checkout 等這些命令只能在 work-tree 中使用,而不能在 git-dir 中使用,由於它們都是用來管理 work-tree 的工具。 [yuhuashi@local:Project]$ ls branches config description HEAD hooks info objects refs # 知道了什麼緣由,那麼咱們就建立一個臨時的 work-tree 目錄,用來切換分支。 [yuhuashi@local:Project]$ cd .. [yuhuashi@local:proj]$ mkdir tmp # 前面說過了,通常狀況下 work-tree 和 git-dir 是放在一塊兒的,也就是 work-tree 是咱們的工程目錄,git-dir 是一個叫作 .git 的文件夾做爲咱們工程文件夾的子目錄。 # 其實 git 也提供了兩個命令來分別指定當前這條命令是在哪一個 git-dir 和 work-tree 上執行,這樣就可讓 git 命令運行在 git-dri 與 work-tree 不在一塊兒的情形了。 [yuhuashi@local:proj]$ git --git-dir=./Project --work-tree=./tmp checkout join Checking out files: 100% (6771/6771), done. Switched to branch 'join' [yuhuashi@local:proj]$ cd Project/ # OK,分支已經成功切換過去了 [yuhuashi@local:Project]$ git branch -a * join masger master [yuhuashi@local:proj]$ rm -rf tmp [yuhuashi@local:proj]$ exit [yuhuashi@local:Project]$
如今遠程默認分支已經切換成功了,咱們有兩種方式繼續爲客戶 A 建立它的 work 分支,一種就是前面第四步所說的:刪掉遠程分支、重命名本地分支、推送到遠程分支。
另外一種方法就是以當前 master 分支爲基準建立一個如出一轍的分支 work,而後刪掉本地的 master 分支和遠程的 origin/master 分支,而後把 work 分支推送到遠程。
LZ選擇了第二種方式,爲何呢?由於想嘗試一下刪除 master 分支的方式 :)
其實刪除 master 分支與刪除其它遠程分支的方式基本相同,惟一不一樣的一點上面已經講過了,就是在刪除以前要修改一下遠程庫的默認分支。
好了,看看 LZ 是怎麼爲用戶 A 建立 work 分支的吧。
(1) 建立新分支 work
[yuhuashi@local:Project]$ git branch -a * join master remotes/origin/HEAD -> origin/master remotes/origin/join remotes/origin/masger remotes/origin/master [yuhuashi@local:Project]$ git checkout master Branch master set up to track remote branch master from origin. Switched to a new branch 'master' [yuhuashi@local:Project]$ git branch -a join * master remotes/origin/HEAD -> origin/master remotes/origin/join remotes/origin/masger remotes/origin/master # 用這種方式建立分支 work,會保留 master 分支的全部 log,可是不會自動切換分支到 work,因此咱們須要手動切換。 [yuhuashi@local:Project]$ git branch work [yuhuashi@local:Project]$ git checkout work Switched to branch 'work' [yuhuashi@local:Project]$
(2)刪除本地 master 分支
[yuhuashi@local:Project]$ git branch -D master
Deleted branch master (was 0981e55).
[yuhuashi@local:Project]$
(3)刪除遠程 master 分支
[yuhuashi@local:Project]$ git push origin :master user@192.168.4.9's password: To ssh://user@192.168.4.9/~/proj/Project - [deleted] master [yuhuashi@local:Project]$ git branch -a * cruise join remotes/origin/HEAD -> origin/master remotes/origin/join remotes/origin/masger [yuhuashi@local:Project]$
能夠看到,通過咱們前面修改了遠程的默認分支以後,如今能夠直接刪掉遠程的 master 分支了。
(4)最後一步,將本地 work 分支推送到遠程庫。
[yuhuashi@local:Project]$ git push origin HEAD:work user@192.168.4.9's password: Counting objects: 7172, done. Delta compression using up to 8 threads. Compressing objects: 100% (4924/4924), done. Writing objects: 100% (7115/7115), 123.00 MiB | 15.07 MiB/s, done. Total 7115 (delta 2007), reused 7074 (delta 1970) To ssh://user@192.168.4.9/~/proj/Project * [new branch] HEAD -> work [ yuhuashi@local:Project]$ git branch -a * cruise join remotes/origin/HEAD -> origin/master remotes/origin/work remotes/origin/join remotes/origin/masger [yuhuashi@local:Project]$
使用 git branch -a 能夠看到,work 分支已經成功在遠程庫上建立了。
最後再看一下遠程庫的狀態:
[yuhuashi@local:Project]$ git remote show origin user@192.168.4.9's password: * remote origin Fetch URL: ssh://user@192.168.4.9/~/proj/Project Push URL: ssh://user@192.168.4.9/~/proj/Project HEAD branch: join # 這裏代表遠程庫的默認分支已是 join 了 Remote branches: work tracked join tracked masger tracked Local refs configured for 'git push': cruise pushes to work (up to date) join pushes to join (up to date) [yuhuashi@local:Project]$ git branch -a * cruise join remotes/origin/HEAD -> origin/master # 這裏指向的分支不該該是 origin/master,而應該是 origin/join 纔對。 remotes/origin/cruise remotes/origin/join remotes/origin/masger [yuhuashi@local:Project]$
多是由於這個庫的分支已經被修改,而咱們本地庫還有一些 git 數據沒有與遠程庫同步,LZ 目前沒有找到同步的辦法,因此咱們先刪掉這個本地庫,從新從遠程庫 clone 一份。
[yuhuashi@local:Project]$ cd .. [yuhuashi@local:~]$ rm -rf Project/ [yuhuashi@local:~]$ git clone ssh://user@192.168.4.9/~/proj/Project Cloning into 'Project'... user@192.168.4.9's password: remote: Counting objects: 8001, done. remote: Compressing objects: 100% (5722/5722), done. remote: Total 8001 (delta 2517), reused 7309 (delta 2042) Receiving objects: 100% (8001/8001), 125.28 MiB | 29.03 MiB/s, done. Resolving deltas: 100% (2517/2517), done. [yuhuashi@local:~]$ cd Project/ [yuhuashi@local:Project]$ git branch -a * join remotes/origin/HEAD -> origin/join remotes/origin/cruise remotes/origin/join remotes/origin/masger [yuhuashi@local:Project]$ git pull user@192.168.4.9's password: Already up-to-date. [yuhuashi@local:Project]$ git checkout -b work origin/work Branch work set up to track remote branch work from origin. Switched to a new branch 'work' [yuhuashi@local:Project]$ git pull user@192.168.4.9's password: Already up-to-date. [yuhuashi@local:Project]$
從上面的流程可知,因爲 join 已是遠程庫的默認分支,因此咱們 clone 了遠程庫以後,git 已經幫助咱們建立好 join 本地分支了。
並且從新下載的代碼中,origin/HEAD 已經能夠正常指向 origin/join 分支了。
參考文獻:
Git查看、刪除、重命名遠程分支和tag
如何刪除-Master-分支
在Git下建立一個空分支
Git系列之二 --- git-dir & work-tree