话说,git pull 和 git fetch 有什么区别?好迷糊……
题外话: 之前看了两遍 codeschool 的 GIT REAL 和 GIT REAL2,有点晕。 又看了本《Git Pro》,感觉清晰了点。 一到用,又忘光光了……
首先,你的每一个操作都是要指明【来源】和【目标】的,而对于 pull 来说,【目标】就是当前分支
其次,你得清楚 git 是有 tracking 的概念的,所谓 tracking 就是把【来源】和【目标】绑定在一起,节省一些操作是需要输入的参数。
那么,假设你的 master 和 develop 都是 tracking 了的,于是:
# 当你在 master 下
$ git pull
# 等于 fetch origin,然后 merge origin/master
# 当你在 develop 下
$ git pull
# 等于 fetch origin,然后 merge origin/develop
$ git pull origin
分支的名字不用打,因为你 tracking 了
Sorry,写这里的时候没有动脑子,错了。tracking 只能是一对一的,没有一个 local branch tracking 多个 remote branch 这么一说,但是多个 remote 是有的。
因此,若你有多个 remote,git pull [remote name]
所做的事情是:
另外,若只有一个 remote,假设叫 origin,那么 git pull
等价于 git pull origin
;平时养成好习惯,没谱的时候都把【来源】带上。
# 当你在 master 下
$ git checkout develop # 切换到 develop,这就是 【目标】
$ git pull origin master # 合并 origin/master,这就是 【来源】
如果你曾经这么推过:git push -u origin master
,那么你执行这条命令时所在的分支就已经 tracking to origin/master 了,-u
的用处就在这里
如果你记不清了:cat .git/config
,给你一张截图,注意红色方框标示的地方(上半部分是 tracking 的,下半部分是 untracking 的),由此可见,tracking 的本质就是指明 pull 的 merge 动作来源。别忘了:pull = fetch + merge。
git fetch
到底干了些啥?注意到红色方框上面的一句了么?
fetch = +refs/heads/*:refs/remotes/origin/*
它指明了 fetch 动作的来源,在本例中就是 叫做 origin
的那个 remote server 下的所有分支
也就是说, git fetch
的操作就是取下上述目标的更新。但是——取下的东西到底在哪儿?
再补一个截图:
就在这里:.git/FETCH_HEAD
。上图特意也做了一个对比,第一次 cat 的时候没有 fetch,第二次 cat 的时候 fetch 了,于是你可以看到其中的区别,之后就可以明白 git pull
的 merge 是如何被触发的了。
git pull
= git fetch + merge
git fetch
拿到了远程所有分支的更新,我用 cat .git/FETCH_HEAD
可以看到其状态,若都是 not-for-merge
则不会有接下来的 merge 动作merge
动作的默认目标是当前分支,若要切换目标,可以直接切换分支merge
动作的来源则取决于你是否有 tracking,若有则读取配置自动完成,若无则请指明【来源】关于不带参数执行 git pull git push 的行为,可以参考之前写的 http://loveky2012.blogspot.com/2012/08/default-behaviour-of-git-pull-and-git-push.html 需翻墙
@chairy11 我只记住了 pull = fetch + merge……其实建个 repo 自己测试一下就可以了 XD
刚学 git 的时候每次我都 fetch + merge,后来就用 git pull 了(因为太懒)
如果搞不清楚的话,老老实实git pull [remote] [branch]
是最稳当的。出了问题 git 也会给 log,很舒服。
#6 楼 @chairy11 既然文字太多不喜欢看,就一步一步来吧
# git init --bare parent.git # 创建一个bare repo用做clone用,相当于server端repo
Initialized empty Git repository in /home/gewang/test/test/parent.git/
# git clone parent.git child1 # clone出第一个工作repo
Cloning into 'child1'...
done.
warning: You appear to have cloned an empty repository.
# cd child1/
# echo a > a && git add . && git commit -m "init drop" # 第一次commit
[master (root-commit) 985c04f] init drop
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 a
# git push origin master:master # 在server端repo中创建master分支
Counting objects: 3, done.
Writing objects: 100% (3/3), 206 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /home/gewang/test/test/parent.git
* [new branch] master -> master
# cd ..
# git clone parent.git child2 # 创建第二个工作repo
Cloning into 'child2'...
done.
# cd child2/
# git checkout -b test # 创建test分支
Switched to a new branch 'test'
# echo abc >> abc && git add . && git commit -m "create test branch"
[test c3d70fd] create test branch
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 abc
# git push origin test:test # 将test分支推送至服务器,注意没有加-u参数
Counting objects: 4, done.
Delta compression using up to 24 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 268 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /home/gewang/test/test/parent.git
* [new branch] test -> test
# git config -l # 查看配置,由于没有使用-u参数,所以没有test分支对应的remote, merge配置项
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
remote.origin.url=/home/gewang/test/test/parent.git
branch.master.remote=origin
branch.master.merge=refs/heads/master
# cd ../child1 # 回到第一个工作repo
# git branch
* master
# git pull origin test # 在master分支上执行 git pull origin test, 可以看到server端test分支的内容被merge进了本地master分支
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From /home/gewang/test/test/parent
* branch test -> FETCH_HEAD
Updating 985c04f..c3d70fd
Fast-forward
abc | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 abc
# git fetch # 执行git fetch为server端test分支建立本地镜像分支
From /home/gewang/test/test/parent
* [new branch] test -> origin/test
# git branch -a
* master
remotes/origin/master
remotes/origin/test
# git checkout test # 虽然本地没有手工创建test分支,但是可以直接checkout,同时git自动为你配置了它与server端test分支的关联。够贴心。
Branch test set up to track remote branch test from origin.
Switched to a new branch 'test'
# git config -l
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
remote.origin.url=/home/gewang/test/test/parent.git
branch.master.remote=origin
branch.master.merge=refs/heads/master
branch.test.remote=origin
branch.test.merge=refs/heads/test
# echo ddd >> ddd && git add . & git commit -m "update branch test" # 在test分支上checkin代码并推送至服务器
[1] 1260
[test 3a078d4] update branch test
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 ddd
[1]+ Done echo ddd >> ddd && git add .
# git push # 不带参数执行git push可以看到本地更新的2个分支都推送了新的change到服务器端。详细解释参考 http://loveky2012.blogspot.com/2012/08/default-behaviour-of-git-pull-and-git-push.html
Counting objects: 4, done.
Delta compression using up to 24 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 295 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To /home/gewang/test/test/parent.git
985c04f..c3d70fd master -> master
c3d70fd..3a078d4 test -> test
# cd ../child2 # 切换到第二个工作repo,执行git pull在将server端change拿到本地后会报错,因为配置里没有关于test分支的mrege信息。(这是由于之前push test分支时没有加-u参数导致的)
# git pull
From /home/gewang/test/test/parent
985c04f..c3d70fd master -> origin/master
c3d70fd..3a078d4 test -> origin/test
You asked me to pull without telling me which branch you
want to merge with, and 'branch.test.merge' in
your configuration file does not tell me, either. Please
specify which branch you want to use on the command line and
try again (e.g. 'git pull <repository> <refspec>').
See git-pull(1) for details.
If you often merge with the same branch, you may want to
use something like the following in your configuration file:
[branch "test"]
remote = <nickname>
merge = <remote-ref>
[remote "<nickname>"]
url = <url>
fetch = <refspec>
See git-config(1) for details.
# git branch --set-upstream test origin/test # 手工配置关联
Branch test set up to track remote branch test from origin.
# git pull # 再次pull即可
Updating c3d70fd..3a078d4
Fast-forward
ddd | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 ddd
除非你有明确的目标,否则不用手动去 merge,否则出问题的可能更大
git pull 对我来说最省事也最不容易出错的
关于是不是省事这点不评论,但就容不容易出错来说 pull 和 merge 没有谁比谁好这说法
#27 楼 @loveky 奇怪,是我的 IDE 延时吗?之前问你的时候,我也已经 git add . 过的啊,但没有。 这次用 git add . ,貌似又好了。
Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: .gitignore
# new file: app/assets/images/icons/avatar.jpeg
# new file: app/assets/images/icons/logo_text.png
# new file: app/assets/images/icons/shortcut.ico
谢谢:)
跟帖子沾个光,最近在 ubuntu 上配置 git 能连通,但是无法从 github 上 clone 和 pull 等很多操作,不知道为什么。 异常: Permission denied (publickey). fatal: Could not read from remote repository. 求大神们帮忙
@chairy11 这个是肯定加了,测试是连通了。目前情况是,我自己的两台电脑都用这一个 github 帐号,老电脑做任何操作都没有问题,新电脑配置后,就测试是连通的,现在什么操作都不行。
#31 楼 @zhangjinzhu 这个工具试图解决的问题是什么呢?
repo 的简介是A tool to simplify working with remote branches
, 但是 rm 操作还会删除本地的分支,会否有歧义?
同时还发现个小问题,如果在 master 上进行 rm master 操作,最后的 branch -d 会有问题吧
解决的问题不是这里写了么:
* rename `branch1` to `branch2`
$ grb mv [branch1] [branch2] [--explain]
* rename current branch to `branch`
$ grb mv branch [--explain]
* add a remote repo
$ grb remote_add `name` `repo path` [--explain]
* remove a remote repo
$ grb remote_rm `name` [--explain]
* delete branch `branch`,default current_branch
$ grb rm [branch] [--explain]
* pull branch `branch`,default current_branch
$ grb pull [branch] [--explain]
* push branch `branch`, default current_branch
$ grb push [branch] [--explain]
* create new branch `branch`
$ grb new [branch] [--explain]
* prune dead remote branches
$ grb prune
// repo 的简介是 A tool to simplify working with remote branches, 但是 rm 操作还会删除本地的分支,会否有歧义?
第一次用最好用 --explain
看看会做什么,熟悉以后就很方便了
// 同时还发现个小问题,如果在 master 上进行 rm master 操作,最后的 branch -d 会有问题吧
明知删除当前所在分支有问题,就用grb remote_rm master
么,或者换个本地分支 ;)
第一次用最好用 --explain 看看会做什么,熟悉以后就很方便了
README 可以写的更详细些,否则不知道如何操作远程分支的用户看了--explain 估计也不知道是什么意思
明知删除当前所在分支有问题,就用 grb remote_rm master 么,或者换个本地分支 ;)
可以在代码里加个判断的 :)