4

注意:这个问题的动机是git更好地理解,而不是解决任何具体问题。IOW,“实现相同目标”的替代方法(即回避标题中的问题)将是无关紧要的。

命令

git pull

...应该等同于序列

git fetch
git merge

可以git pull <REMOTE> <BRANCH>(即带有远程和从中提取的分支的显式参数)分解为类似的 afetch后跟 a的序列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.,并git-log显示它HEAD保持在尝试之前的相同提交git merge。为了确认这一点,我将git fetch ...andgit 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>


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

#!/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

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

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

从上面的输出可以看出,

  1. 接近末尾的 fetch + merge 序列对输出没有影响git log...
  2. git merge命令的输出是Already up-to-date.,即使不是这种情况(远程和本地存储库各有一个提交分歧)。
  3. 在此“合并”之后,本地分支 ( master) 是跟踪分支 ( remrepo/master) 之前的一个提交。
  4. 与 fetch + merge 序列相比,pull 做的是正确的事情(即更新跟踪分支,并执行合并),即使两组命令都接收到完全相同的信息。
4

3 回答 3

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>

从远程引入有关所有参考的信息通常没有害处(除非您有一些特定要求不这样做)。如果您对此感到满意,则git fetch可以在没有任何参考规范的情况下运行第一个,即

git fetch <REMOTE>

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

git rebase <REMOTE>/<REMOTE_BRANCH_NAME>

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

感谢 kostix 指出local-branch:remote-branch语法git fetch

于 2013-03-18T19:14:28.123 回答
1

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

请重新阅读git-fetch 手册

于 2013-03-19T14:31:07.230 回答
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>

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

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

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

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

git merge <SHA1>
于 2013-03-19T00:04:32.920 回答