- 远程分支的概念
- 查看、添加、重命名、删除远程仓库
- 本地分支相对远程分支的移动
- 跟踪远程分支
- 合并、推送、改跟、拉取、删除远程分支
- 变基的使用场景,目的及原理,变基 vs. 合并
- Linux
首先我们先克隆一个仓库
$ git clone git@github.com:Mr-Awakened/Programming-Road.git
git clone命令会拉取远程仓库的所有数据,并创建两个指针,一个指针是master,另一个是origin/master,都初始化为与远程仓库的master指向相同的提交对象。其中master是本地分支,在我们使用git commit命令进行提交时会不断向前推进,也会随git merge、git reset等命令发生移动;而origin/master被称为远程分支,用来标识远程仓库中的master分支在本地提交历史中的位置,自己是不能移动的。
远程仓库名字“origin”与分支名字“master”一样,在Git中没有特别的含义。"master"是当你运行git init时默认的起始分支名字,“origin”是运行git clone时默认的远程仓库名字,原因仅仅是它们的广泛使用。
我们可以调用git remote查看所有远程仓库
$ git remote -v
origin git@github.com:Mr-Awakened/Programming-Road.git (fetch)
origin git@github.com:Mr-Awakened/Programming-Road.git (push)
使用*git remote add <shortname> <url>*添加远程仓库
$ git remote add algorithm git@github.com:Mr-Awakened/algorithmcpp.git
添加远程仓库实际上是针对该Git仓库的配置,我们可以在该仓库的.git/config文件中找到配置信息
$ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
url = git@github.com:Mr-Awakened/Programming-Road.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[remote "algorithm"]
url = git@github.com:Mr-Awakened/algorithmcpp.git
fetch = +refs/heads/*:refs/remotes/algorithm/*
与此形成对比的是,之前我们使用
git config --global user.name "your-name"
git config --global user.email "your-email"
配置的用户名和邮箱是对所有仓库的通用配置,配置信息可以在~/.gitconfig文件中找到
$ cat ~/.gitconfig
[user]
name = LYC-BF
email = 540536130@qq.com
[core]
editor = vim
我们也可以使用git remote rename <oldname> <newname> 重命名远程仓库,使用git remote rm <shortname> 删除远程仓库
$ git remote rename algorithm alg
$ git remote rm alg
前面已经提到,远程分支是用来标识远程仓库中的分支在本地提交历史中的位置,只要我们不去更新这个标识,它是不会发生移动的。我们可以验证一下。
先查看当前所有分支的提交历史
$ git log --oneline --decorate --graph --all
* 70f3ab0 (HEAD -> master, origin/master) finish Git学习(二)
* ee7aad6 rename
* 74b75e4 revise abstract for Git learn
* 5bac74c fix path name
* 3adc774 add git basics
* a4f6645 not play game agagin
* ca39164 unspeakable
* 8c63007 delete one source file
* 00b9fbf first commit
此时master与origin/master指向同一提交对象。我们进行一次新的提交
$ vim README.md
$ git commit -a -m 'revise introduction'
$ git log --oneline --decorate --graph --all
* 4399b9f (HEAD -> master) revise introduction
* 70f3ab0 (origin/master, origin/HEAD) finish Git学习(二)
* ee7aad6 rename
* 74b75e4 revise abstract for Git learn
* 5bac74c fix path name
* 3adc774 add git basics
* a4f6645 not play game agagin
* ca39164 unspeakable
* 8c63007 delete one source file
* 00b9fbf first commit
提交后master相比于origin/master领先了一个提交对象。
我们再使用git reset命令移动HEAD指针所指向的分支试试。
$ git reset --soft HEAD~3
$ git log --oneline --decorate --graph --all
* 70f3ab0 (origin/master, origin/HEAD) finish Git学习(二)
* ee7aad6 rename
* 74b75e4 (HEAD -> master) revise abstract for Git learn
* 5bac74c fix path name
* 3adc774 add git basics
* a4f6645 not play game agagin
* ca39164 unspeakable
* 8c63007 delete one source file
* 00b9fbf first commit
此时master相比于origin/master落后了两个提交对象。
将一个本地分支与一个远程分支作比较,观察它们在提交上领先或者落后了多少,这就是“跟踪”的概念。在我们使用git clone命令克隆一个仓库时,Git会让创建的master分支去跟踪origin/master分支。有时候,我们希望跟踪其他分支,或者自定义跟踪远程分支的本地分支名,这个时候我们就可以使用 git checkout -b <local-branch-name> <remote-branch-name> 命令
$ git checkout -b latter origin/master
M README.md
Branch 'latter' set up to track remote branch 'master' from 'origin'.
Switched to a new branch 'latter'
如果想查看所有本地分支对应的跟踪情况,可以使用git branch -vv命令
$ git branch -vv
* latter 70f3ab0 [origin/master] finish Git学习(二)
master 74b75e4 [origin/master: behind 2] revise abstract for Git learn
由于之前的git reset操作,master分支落后origin/master分支两个提交对象。
合并远程分支与合并本地分支的操作一模一样
$ git checkout master
$ git merge origin/master
Updating 74b75e4..70f3ab0
Fast-forward
我们可以使用 git push <remote-name> <local-branch-name> 向远程仓库推送特定的本地分支
$ git push origin latter
Total 0 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'latter' on GitHub by visiting:
remote: https://github.com/Mr-Awakened/Programming-Road/pull/new/latter
remote:
To github.com:Mr-Awakened/Programming-Road.git
* [new branch] latter -> latter
在建立跟踪关系后,可以后续进行修改
$ git branch -vv
latter 70f3ab0 [origin/master] finish Git学习(二)
* master 70f3ab0 [origin/master] finish Git学习(二)
$ git checkout latter
$ git branch -u origin/latter latter
$ git branch -vv
* latter 70f3ab0 [origin/latter] finish Git学习(二)
master 70f3ab0 [origin/master] finish Git学习(二)
如果我们希望该分支不跟踪任何远程分支怎么办?先删除这个本地的远程分支试试
$ git branch -dr origin/latter
Deleted remote-tracking branch origin/latter (was 70f3ab0).
$ git branch -a
* latter
master
remotes/origin/HEAD -> origin/master
remotes/origin/master
$ git branch -vv
* latter 70f3ab0 [origin/latter: gone] finish Git学习(二)
master 70f3ab0 [origin/master] finish Git学习(二)
可以看到,本地的远程分支标签虽然没了,但是跟踪关系还在,这让人很难受,我们可以使用git config命令去删除这些关系
$ git config --unset branch.latter.remote
$ git config --unset branch.latter.merge
$ git branch -vv
* latter 70f3ab0 finish Git学习(二)
master 70f3ab0 [origin/master] finish Git学习(二)
使用 git fetch <remote-name> 来抓取远程仓库的数据,并更新远程分支标识
$ git log --oneline --decorate --graph --all
* 70f3ab0 (HEAD -> latter, origin/master, origin/HEAD, master) finish Git学习(二)
* ee7aad6 rename
* 74b75e4 revise abstract for Git learn
* 5bac74c fix path name
* 3adc774 add git basics
* a4f6645 not play game agagin
* ca39164 unspeakable
* 8c63007 delete one source file
* 00b9fbf first commit
$ git fetch origin
From github.com:Mr-Awakened/Programming-Road
* [new branch] latter -> origin/latter
$ git log --oneline --decorate --graph --all
* 70f3ab0 (HEAD -> latter, origin/master, origin/latter, origin/HEAD, master) finish Git学习(二)
* ee7aad6 rename
* 74b75e4 revise abstract for Git learn
* 5bac74c fix path name
* 3adc774 add git basics
* a4f6645 not play game agagin
* ca39164 unspeakable
* 8c63007 delete one source file
* 00b9fbf first commit
可以看出,在抓取数据后,我们在本地恢复了远程分支标识origin/latter,并使其指向其在远程仓库中对应的提交对象的位置。
现在,我们的远程分支origin/latter与远程分支origin/master指向同一个提交对象,也就是说origin/latter分支已经没有存在的必要了。我们可以运行带有 --delete 选项的git push命令来删除一个远程分支
$ git push origin --delete latter
To github.com:Mr-Awakened/Programming-Road.git
- [deleted] latter
$ git branch -vv
* latter 70f3ab0 finish Git学习(二)
master 70f3ab0 [origin/master] finish Git学习(二)
可以看出,带有 --delete 选项的git push命令只是删除了远程仓库中的远程分支,是不会删除对应的本地分支的。
首先,我们先使用git reset和git commit命令使得两个分支产生分叉
$ git reset --hard HEAD~1
$ vim src/hello/hello.h
$ git commit -a -m 'add public specifier'
$ git log --oneline --decorate --graph --all
* 829fb1b (HEAD -> latter) add public specifier
| * 70f3ab0 (origin/master, origin/HEAD, master) finish Git学习(二)
|/
* ee7aad6 rename
* 74b75e4 revise abstract for Git learn
* 5bac74c fix path name
* 3adc774 add git basics
* a4f6645 not play game agagin
* ca39164 unspeakable
* 8c63007 delete one source file
* 00b9fbf first commit
假设我们希望在master分支中合并latter分支所作的修改,但是又不希望使用git merge命令使得最新的提交对象有两个父对象,也就是说我们希望提交历史变得简洁,我们就可以使用git rebase命令进行变基。所谓变基,就是找到两个分支的最近共同祖先,然后提取当前分支相对于该共同祖先修改,然后将该修改应用在作为基底的分支上。
$ git checkout master
$ git rebase latter
First, rewinding head to replay your work on top of it...
Applying: finish Git学习(二)
$ git log --oneline --decorate --graph --all
* 8990000 (HEAD -> master) finish Git学习(二)
* 829fb1b (latter) add public specifier
| * 70f3ab0 (origin/master, origin/HEAD) finish Git学习(二)
|/
* ee7aad6 rename
* 74b75e4 revise abstract for Git learn
* 5bac74c fix path name
* 3adc774 add git basics
* a4f6645 not play game agagin
* ca39164 unspeakable
* 8c63007 delete one source file
* 00b9fbf first commit
变基(git rebase)与合并(git merge)是整合来自不同修改的两张方法。那哪种方式更好呢?
有一种观点认为,仓库的提交历史记录着实际发生过什么,因此使用变基改变提交历史就是一种亵渎,我们应该使用合并,哪怕合并产生的提交历史看起来非常复杂。
另一种观点认为,提交历史是项目过程中发生的故事,没人会出版一本书的草稿,因此我们应该使用变基来讲一个好故事。
具体情况下,仁者见仁,智者见智。