我在一个有两个分支a和B的项目上工作。我通常在分支a上工作,并合并分支B中的内容。对于合并,我通常会这样做:
git merge origin/branchB
但是,我也希望保留分支B的本地副本,因为我可能会偶尔检查分支,而不必首先与分支a合并。为此,我会:
git checkout branchB
git pull
git checkout branchA
有没有一种方法可以在一个命令中完成上述操作,而不必来回切换分支?我应该使用gitupdate-ref吗?怎样
我在一个有两个分支a和B的项目上工作。我通常在分支a上工作,并合并分支B中的内容。对于合并,我通常会这样做:
git merge origin/branchB
但是,我也希望保留分支B的本地副本,因为我可能会偶尔检查分支,而不必首先与分支a合并。为此,我会:
git checkout branchB
git pull
git checkout branchA
有没有一种方法可以在一个命令中完成上述操作,而不必来回切换分支?我应该使用gitupdate-ref吗?怎样
只有当合并是快进时,才能执行此操作。如果不是,那么git需要签出文件,以便合并它们!
仅为快进操作:
git fetch <branch that would be pulled for branchB>
git update-ref -m "merge <commit>: Fast forward" refs/heads/<branch> <commit>
其中<commit>是提取的commit,即您要快进的commit。这基本上就像使用gitbranch-f移动分支一样,只是它还将其记录在reflog中,就像您实际执行了合并一样。
拜托,拜托,请不要为不快速前进的事情而这样做,否则您只会将分支重置为另一个提交。(要检查,请查看gitmerge-base<branch><commit>是否给出了分支的SHA1。)
不,没有。需要签出目标分支,以便解决冲突(如果Git无法自动合并冲突)。
但是,如果合并是一个可以快速进行的合并,则不需要检查目标分支,因为实际上不需要合并任何内容-只需更新分支以指向新的头部引用即可。您可以使用gitbranch-f执行此操作:
git branch -f branch-b branch-a
将更新branch-b以指向branch-a的头部。
-f选项代表--force,这意味着branch-b将被覆盖。
警告:更安全的选择是使用git fetch,它只允许快进。
此方法可按如下方式使用:
git branch -f branch-b branch-b@{Upstream}
或更短
git branch -f branch-b branch-b@{U}
强制更新一个分支,而不检查它(例如,如果它们在重基之后已经分叉)
正如Amber所说,快进合并是唯一可以想象得到的情况。任何其他合并都可能需要经过整个三方合并,应用补丁,解决冲突-这意味着需要有文件。
我身边正好有一个脚本:在不接触工作树的情况下进行快速向前合并(除非您要合并到HEAD)。它有点长,因为它至少有点健壮——它检查以确保合并是快速前进的,然后在不检查分支的情况下执行它,但会产生与之前相同的结果——您可以看到更改的diff-stat摘要,并且reflog中的条目与快速前进的合并完全相同,而不是使用branch-f时得到的“重置”结果。如果将其命名为gitmergeff并将其放到bin目录中,则可以将其作为git命令调用:gitmergeff。
#!/bin/bash
_usage() {
echo "Usage: git merge-ff <branch> <committish-to-merge>" 1>&2
exit 1
}
_merge_ff() {
branch="$1"
commit="$2"
branch_orig_hash="$(git show-ref -s --verify refs/heads/$branch 2> /dev/null)"
if [ $? -ne 0 ]; then
echo "Error: unknown branch $branch" 1>&2
_usage
fi
commit_orig_hash="$(git rev-parse --verify $commit 2> /dev/null)"
if [ $? -ne 0 ]; then
echo "Error: unknown revision $commit" 1>&2
_usage
fi
if [ "$(git symbolic-ref HEAD)" = "refs/heads/$branch" ]; then
git merge $quiet --ff-only "$commit"
else
if [ "$(git merge-base $branch_orig_hash $commit_orig_hash)" != "$branch_orig_hash" ]; then
echo "Error: merging $commit into $branch would not be a fast-forward" 1>&2
exit 1
fi
echo "Updating ${branch_orig_hash:0:7}..${commit_orig_hash:0:7}"
if git update-ref -m "merge $commit: Fast forward" "refs/heads/$branch" "$commit_orig_hash" "$branch_orig_hash"; then
if [ -z $quiet ]; then
echo "Fast forward"
git diff --stat "$branch@{1}" "$branch"
fi
else
echo "Error: fast forward using update-ref failed" 1>&2
fi
fi
}
while getopts "q" opt; do
case $opt in
q ) quiet="-q";;
* ) ;;
esac
done
shift $((OPTIND-1))
case $# in
2 ) _merge_ff "$1" "$2";;
* ) _usage
esac
注:如果有人看到该脚本有任何问题,请评论!这是一份写就忘的工作,但我很乐意改进它。
另一种公认的蛮力方法是重新创建分支:
git fetch remote
git branch -f localbranch remote/remotebranch
这将丢弃本地过时的分支,并重新创建一个同名分支,因此请谨慎使用。。。
对于许多情况(例如合并),您可以只使用远程分支,而不必更新本地跟踪分支。在reflog中添加一条消息听起来有些过分,而且会让它变得更快。为了更容易恢复,请在git配置中添加以下内容
[core]
logallrefupdates=true
然后键入
git reflog show mybranch
查看分支机构的近期历史
简短的回答
只要您正在进行快速合并,就可以简单地使用
git fetch <remote> <sourceBranch>:<destinationBranch>
示例:
# Merge local branch foo into local branch master,
# without having to checkout master first.
# Here `.` means to use the local repository as the "remote":
git fetch . foo:master
# Merge remote branch origin/foo into local branch foo,
# without having to checkout foo first:
git fetch origin foo:foo
虽然Amber的答案也适用于快进情况,但以这种方式使用git fetch比强制移动分支引用更安全,因为只要在refspec中不使用+,git fetch就会自动防止意外的非快进。
答案很长
如果不先签出a,就不能将分支B合并到分支a,否则会导致非快速合并。这是因为需要一个工作副本来解决任何潜在的冲突。
然而,在快速合并的情况下,这是可能的,因为根据定义,这种合并永远不会导致冲突。要在不首先检查分支的情况下执行此操作,可以使用gitfetch和refspec。
如果您签出了另一个分支功能,以下是更新master(不允许非快进更改)的示例:
git fetch upstream master:master
这个用例非常常见,您可能需要在git配置文件中为它创建一个别名,如下所示:
[alias]
sync = !sh -c 'git checkout --quiet HEAD; git fetch upstream master:master; git checkout --quiet -'
此别名的作用如下:
gitcheckout HEAD:这将使您的工作副本进入分离的头状态。如果您想在碰巧签出master时更新它,这很有用。我认为这是必要的,因为否则master的分支引用不会移动,但我不记得这是否真的就在我的头顶上。gitfetchupstream-master:master:这将快速将本地主机转发到与upstream/master相同的位置。gitcheckout-签出您以前签出的分支(在本例中-就是这么做的)。
gitfetch用于(非)快进合并的语法
如果您希望fetch命令在非快速更新时失败,那么只需使用表单的refspec
git fetch <remote> <remoteBranch>:<localBranch>
如果您希望允许非快速更新,那么在refspec前面添加一个+:
git fetch <remote> +<remoteBranch>:<localBranch>
请注意,您可以使用传递本地回购作为“远程”参数。:
git fetch . <sourceBranch>:<destinationBranch>
文件
从解释此语法的git fetch文档中(重点是我的):
<参考规范><refspec>参数的格式是可选的加号+,后跟源ref<src>,后跟冒号:,后跟目标ref<dst>。获取与<src>匹配的远程引用,如果<dst>不是空字符串,则使用<src>快速转发与之匹配的本地引用。如果使用可选的加号+,则即使不会导致快速更新,也会更新本地ref。
另请参见
Git签出和合并而不接触工作树合并而不更改工作目录
输入git前向合并:
无需签出目标,git forward merge<source><destination>将源合并到目标分支。
https://github.com/schuyler1d/git-forward-merge
仅适用于自动合并,如果存在冲突,则需要使用常规合并。
在您的情况下,您可以使用
git fetch origin branchB:branchB
它可以满足您的需要(假设合并是快速进行的)。如果由于需要非快速合并而无法更新分支,那么这将安全地失败,并显示一条消息。
这种形式的获取也有一些更有用的选项:
git fetch <remote> <sourceBranch>:<destinationBranch>
请注意,<remote>可以是本地存储库,<sourceBranch>可以是跟踪分支。因此,您可以在不访问网络的情况下更新本地分支,即使它未检出。
目前,我的上游服务器访问是通过一个缓慢的VPN,所以我定期连接,gitfetch更新所有远程设备,然后断开连接。如果远程主机发生了变化,我可以
git fetch . remotes/origin/master:master
即使我目前有其他分支机构结账,也能安全地让我的本地主管了解最新情况。无需网络访问。
我为每天在项目中遇到的类似用例编写了一个shell函数。这基本上是一个快捷方式,可以让本地分支机构在打开PR之前保持与公共分支机构的最新状态,如开发等。
即使你不想使用结账,也要发布这个,以防其他人不介意这种限制。
glmh(“git pull and merge here”)将自动签出分支B,拉取最新的分支,重新签出分支A,并合并分支B。
不需要保留branchA的本地副本,但可以通过在签出branchB之前添加一个步骤来轻松修改。类似于。。。
git branch ${branchA}-no-branchB ${branchA}
对于简单的快进合并,这将跳到提交消息提示符。
对于非快速合并,这将使分支处于冲突解决状态(您可能需要进行干预)。
要进行设置,请添加到.bashrc或.zshrc等:
glmh() {
branchB=$1
[ $# -eq 0 ] && { branchB="develop" }
branchA="$(git branch | grep '*' | sed 's/* //g')"
git checkout ${branchB} && git pull
git checkout ${branchA} && git merge ${branchB}
}
用法:
# No argument given, will assume "develop"
> glmh
# Pass an argument to pull and merge a specific branch
> glmh your-other-branch
注意:这不足以将分支名称以外的参数传递给gitmerge
另一种有效的方法是:
git fetch
git branch -d branchB
git branch -t branchB origin/branchB
因为它是小写-d,所以只有在数据仍然存在的情况下才会删除它。这与@kkoehne的答案相似,只是它不强制。由于-t,它将再次设置遥控器。
我有一个与OP稍有不同的需求,那就是在合并一个拉取请求之后,创建一个新的特性分支,脱离开发(或主)。这可以在一个命令行中完成,无需强制,但它不会更新本地开发分支。这只是检查一个新的分支机构并使其基于起源/发展的问题:
git checkout -b new-feature origin/develop
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]
你可以尝试git worktree让两个分支并排打开,这听起来可能是你想要的,但与我在这里看到的其他一些答案非常不同。
通过这种方式,您可以在同一个git repo中有两个单独的分支进行跟踪,因此您只需获取一次即可在两个工作树中获取更新(而不必分别获取两次git clone和git pull)
Worktree将为您的代码创建一个新的工作目录,您可以在其中同时签出不同的分支,而不是在原地交换分支。
当您想要删除它时,可以使用
git worktree remove [-f] <worktree>
不需要git签出,就可以进行任何合并,甚至是非快速合并。@grego的工作树回答是一个很好的提示。要对此进行扩展,请执行以下操作:
cd local_repo
git worktree add _master_wt master
cd _master_wt
git pull origin master:master
git merge --no-ff -m "merging workbranch" my_work_branch
cd ..
git worktree remove _master_wt
现在,您已经将本地工作分支合并到本地主分支,而无需切换签出。
如果您想保持与要合并的分支相同的树(即,不是真正的“合并”),可以这样做。
# Check if you can fast-forward
if git merge-base --is-ancestor a b; then
git update-ref refs/heads/a refs/heads/b
exit
fi
# Else, create a "merge" commit
commit="$(git commit-tree -p a -p b -m "merge b into a" "$(git show -s --pretty=format:%T b)")"
# And update the branch to point to that commit
git update-ref refs/heads/a "$commit"
我在这个问题上遗漏了一些东西,如果你没有打算去做的话,为什么你需要去当地的分行结账?在您的示例中,您只想在保留分支A的同时更新本地分支B?
我最初假设您这样做是为了获取origin/branchB,您已经可以使用gitfetch来获取,所以这个答案确实基于此。在您需要对branchB进行操作之前,不需要拉动它,并且当您需要从origin/branchB合并时,始终可以获取origin。
如果您想跟踪branchB在某个时间点的位置,可以从最新获取的origin/branchB创建一个标记或另一个分支。
因此,您需要从原点/分支B进行合并:
git fetch
git merge origin/branchB
下次你需要在分支B上工作时:
git checkout branchB
git pull
此时,您将获得更新的本地副本。虽然有一些方法可以在不结账的情况下完成,但这很少有用,而且在某些情况下可能不安全。对此已有答案。
详细答案:
gitpull执行fetch+merge。这与下面的两个命令大致相同,其中<remote>通常是原点(默认),远程跟踪分支以<remote’/开头,后跟远程分支名称:
git fetch [<remote>]
git merge @{u}
@{u}符号是为当前分支配置的远程跟踪分支。如果branchB跟踪origin/branchB,那么来自branchB的@{u}与键入origin/bbranchB相同(请参阅git-rev-parse-help获取更多信息)。
由于您已经与origin/branchB合并,所以只缺少gitfetch(可以从任何分支运行)来更新远程跟踪分支。
不过请注意,如果在拉入本地分支B时创建了任何合并,那么您应该在从分支B拉入之后将分支B合并到分支a中(并最终将更改推回到origin/branchB,但只要它们快速前进,它们将保持不变)。
请记住,在您切换到本地分支并进行实际拉取之前,不会更新本地分支B,但是,只要没有向该分支添加本地提交,它就将保持到远程分支的快速前进。
对于许多GitFlow用户来说,最有用的命令是:
git fetch origin master:master --update-head-ok
git fetch origin dev:dev --update-head-ok
--update-headok标志允许在dev或master分支上使用相同的命令。
gitconfig中一个方便的别名:
[alias]
f=!git fetch origin master:master --update-head-ok && git fetch origin dev:dev --update-head-ok