如何将git pull <remote> <branch>分解为fetch + merge?</branch> </remote>

时间:2013-03-18 18:19:22

标签: git

注意:此问题的动机是更好地理解git,而不是解决任何具体问题。 IOW,“实现同样的事情”的替代方式(即在标题中侧面提出问题)将是不重要的。

命令

git pull

...应该等同于序列

git fetch
git merge

可以git pull <REMOTE> <BRANCH>(即使用远程的显式参数和从中拉出的分支)被分解为fetch后跟merge的类似序列吗?

我认为fetch部分只是

git fetch <REMOTE> <BRANCH>

...但是,如果是这样,我无法找出正确的git merge ...来跟随它。


我尝试过“明显”的事情。例如,如果我运行git branch -r,则输出会在分支中列出<REMOTE>/<BRANCH>,因此我尝试git merge -m 'some message' <REMOTE>/<BRANCH>,但git回复Already up-to-date.,{{1 }}表示git-log保持与尝试HEAD之前相同的提交。为了确认这一点,我将git mergegit fetch ...git merge ...的电话括起来,如下所示:

git log ...

两次拨打git log --all --oneline --graph --decorate -10 git fetch <REMOTE> <BRANCH> git merge -m 'some message' <REMOTE>/<BRANCH> git log --all --oneline --graph --decorate -10 时产生的输出相同,它们都显示本地git log ...位于<BRANCH>之前。


以下玩具示例以<REMOTE>/<BRANCH>脚本的形式再现了我上面描述的结果。 (该脚本在Ubuntu Linux上进行了测试; YMMV。)

/bin/sh

如果您运行它,输出将类似于以下内容:

#!/bin/sh

BASEDIR=/tmp/gittest
REMOTENAME=remrepo
REMOTEURL="$BASEDIR/$REMOTENAME"
BRANCHNAME=test
BRANCHNAME=master

rm -rf $REMOTEURL
mkdir -p $REMOTEURL

rm -rf $BASEDIR/clone1 $BASEDIR/clone2

git init --bare -q $REMOTEURL/.git
git clone -q -o $REMOTENAME $REMOTEURL $BASEDIR/clone1
git clone -q -o $REMOTENAME $REMOTEURL $BASEDIR/clone2

pushd $BASEDIR/clone1 >/dev/null
git checkout -qb $BRANCHNAME
echo $RANDOM >> random1.txt
git add .
git commit -qam "$(date -Ins)"
git push -q $REMOTENAME $BRANCHNAME

pushd $BASEDIR/clone2 >/dev/null
git pull -q $REMOTENAME
git checkout -q $BRANCHNAME
echo $RANDOM >> random2.txt
git add .
git commit -qam "$(date -Ins)"
git push -q $REMOTENAME $BRANCHNAME

echo
echo 'git log --all --oneline --decorate --graph :'
git log --all --oneline --decorate --graph
echo

pushd >/dev/null
git checkout -q $BRANCHNAME
echo $RANDOM >> random1.txt
git commit -qam "$(date -Ins)"

echo 'git log --all --oneline --decorate --graph :'
git log --all --oneline --decorate --graph
echo

git fetch -q $REMOTENAME $BRANCHNAME
git merge -m "$(date -Ins)" $REMOTENAME/$BRANCHNAME

echo
echo 'git log --all --oneline --decorate --graph :'
git log --all --oneline --decorate --graph

git pull -q --no-edit $REMOTENAME $BRANCHNAME

echo
echo 'git log --all --oneline --decorate --graph :'
git log --all --oneline --decorate --graph

从上面的输出中可以看出,

  1. 靠近结尾的fetch + merge序列对warning: You appear to have cloned an empty repository. warning: You appear to have cloned an empty repository. git log --all --oneline --decorate --graph : * 2326793 (HEAD, remrepo/master, master) 2013-03-19T10:56:42,838038000-0400 * 34ea848 2013-03-19T10:56:42,360743000-0400 git log --all --oneline --decorate --graph : * 81cb43f (HEAD, master) 2013-03-19T10:56:43,057198000-0400 * 34ea848 (remrepo/master) 2013-03-19T10:56:42,360743000-0400 Already up-to-date. git log --all --oneline --decorate --graph : * 81cb43f (HEAD, master) 2013-03-19T10:56:43,057198000-0400 * 34ea848 (remrepo/master) 2013-03-19T10:56:42,360743000-0400 git log --all --oneline --decorate --graph : * e60b993 (HEAD, master) Merge branch 'master' of /tmp/gittest/remrepo |\ | * 2326793 2013-03-19T10:56:42,838038000-0400 * | 81cb43f 2013-03-19T10:56:43,057198000-0400 |/ * 34ea848 (remrepo/master) 2013-03-19T10:56:42,360743000-0400
  2. 的输出没有影响
  3. git log...命令的输出为git merge,即使不是这种情况(远程和本地存储库已经分别进行了一次提交)。
  4. 在此“合并”之后,本地分支(Already up-to-date.)是跟踪分支(master)之前的一次提交。
  5. 与fetch + merge序列相反,pull是正确的事情(即它更新跟踪分支,并执行合并),即使两组命令都收到了完全相同的信息。

3 个答案:

答案 0 :(得分:1)

正如git pull的手册页所说:

  

将来自远程存储库的更改合并到当前分支中。   在默认模式下,git pull是git fetch的简写,后跟   git merge FETCH_HEAD。

     

更确切地说,git pull使用给定的参数运行git fetch   调用git merge将检索到的分支头合并到当前   科。使用--rebase,它运行git rebase而不是git merge。

基于此(假设您没有使用--rebase),git pull命令应该几乎等同于:

# Fetch the info about the branch from the remote
git fetch <REMOTE> <BRANCH>:<REMOTE>/<BRANCH>

# Switches your working copy to the branch that you would like the
# changes to be merged into
git checkout <LOCAL_BRANCH_NAME>

# Merge the changes from <REMOTE>/<REMOTE_BRANCH_NAME> into your
# currently checked out branch which should <LOCAL_BRANCH_NAME>
# after the previous checkout command
git merge <REMOTE>/<REMOTE_BRANCH_NAME>

从遥控器引入有关所有引用的信息通常没有害处(除非您有一些特定要求)。如果您对此感到满意,可以在没有任何refspec的情况下运行第一个git fetch,即

git fetch <REMOTE>

如果您使用--rebase选项,则git merge命令将替换为以下git rebase命令:

git rebase <REMOTE>/<REMOTE_BRANCH_NAME>

PS:与git merge不同,git rebase也采用可选的目标分支参数。如果您没有指定,它将使用当前签出的分支。

感谢kostix指向git fetch

local-branch:remote-branch语法

答案 1 :(得分:1)

我认为问题在于您的玩具示例使用git fetch <repository> <branch> - 仅由分支名称组成的 refspec 被解释为远程的分支名称,该分支被提取,其提示提交的SHA-1名称被写入.git/FETCH_HEAD文件;没有本地分支更新,因为refspec错过了“:destination”部分(要使用所提取内容更新的本地分支)。所以基本上你的git fetch干涸了。

请重新阅读the git-fetch manual

答案 2 :(得分:0)

简短的回答是......

git pull <remote> <branch>

在功能上等同于:

git fetch <remote>
git merge <remote>/<remote_branch>

如果您收到回复说一切都是最新的,那么您要么没有运行git fetch <remote>,要么实际上是最新的。


编辑: 在阅读了一些关于不同答案的评论之后,听起来您已经运行了git merge <remote>/<branch>命令但未获得更新,即使您已经验证它们处于不同的提交中。尝试这些步骤(可能重复,但试一试)

git log -1 <local_branch>
git log -1 <remote>/<remote_branch>

如果这两者不相同,请尝试以下方法 (如果相同,那么你已经是最新的!)

git checkout <local_branch>
git merge <remote>/<remote_branch>

如果它仍然说你是最新的,那么也许你本地有提交你需要推高,也许这就是为什么你看到不同的提交。

git push <remote> <local_branch>:<remote_branch>

使用分支:分支格式只是对您尝试推送的内容非常具体的一种方式。它说推:

现在再次检查这些日志..

git log -1 <local_branch>
git log -1 <remote>/<remote_branch>

如果你仍然看到一个不同的提交 - 那么你确实有一个边缘案例。尝试复制您为登录/获取的提交哈希。然后直接将其合并到您的本地分支

git merge <SHA1>