不接触工作树的Git checkout和合并

27

假设我有一个功能分支,我在推送我的更改之前将上游更改合并到该分支中:

git branch feature1
... [edit my code]
... [commit]
git fetch origin master
git merge fetch_head [or rebase]
... [resolve conflicts]
... [build and test code]

现在我想要推送我的更改。通常的做法是:

git checkout master [changes a bunch of working tree files]
git merge feature1  [changes the same files right back]

这段代码很好用,但编译器会认为很多文件需要重新构建,即使内容没有变化。有没有一种检出和合并的方法,在这种情况下不更改工作树呢?

类似于:

git checkout master --merge-branch feature1

编辑:

我只是在谈论快速向前合并,根据定义,这种合并不会改变文件的状态。


https://dev59.com/UHM_5IYBdhLWcg3wq1CF - Ciro Santilli OurBigBook.com
4个回答

45

一种简单且安全的方法——无需推送或强制更新——就是将feature1拉取到master分支:

(feature1)$ git fetch . feature1:master
From .
   4a6000d..8675309  feature1   -> master

关键是使用.来获取本地的feature1引用。这比强制更新主分支更安全,因为它确保更新是快进式的。(有关详细信息,请参见git-fetch文档中的参数。)

现在feature1和master相同,它们之间的切换不会影响任何文件:

(feature1)$ git checkout master
Switched to branch 'master'
(master)$

谢谢,这个可行。虽然我不明白 fetch 部分在做什么。 - towi
1
@towi,你实际上是在执行一个从自己进行的 git pull 操作,但没有更新工作副本(git pull 基本上是 git fetchgit merge 的组合)。 - dlitz
4
这应该是被接受的答案,我在不同情境下都会一再回来看它 :) - Andrew Mao

9

[编辑] 这只是一个部分解决方案/变通方法。请参见下面@djpohly的实际答案。

首先,您可以从任何地方推送。无论您检出了什么,或者您想要推送的提交是否在主分支中。

git push REMOTE_REPO feature1:master

请参阅git help push

提示: git push remoteRepo localRef:remoteRef

如果不想干扰您当前的工作副本,又想将master分支同步到最新状态,可以使用以下命令强制同步:

# (while still on feature1 branch)
git checkout -B master origin/master

但是这将对主分支进行硬重置。也就是说,它不会检查快速转发。


这是正确的,解决了一半的问题。即便如此,在功能分支完成后我也不能真正留在该分支上。因为我基本上想回到主分支,所以我仍然会有这个(相对较小的)问题。 - Tomas Lundell
1
好的,你问题的另一部分(关于希望修改文件时间戳变为正确值)实际上是无解的。Git不知道哪些文件何时被编译,因此每次更改文件时都会更新时间戳。这会导致一些额外的重新编译,但这比有时应该重新编译时却没有重新编译要好得多。 - JasonWoof
2
也许您想在计算机上再克隆一个副本,这样您就可以同时检出两个副本?如果您关心空间,可以使两个本地副本共享存储库存储。 - JasonWoof
2
我想,对于快速合并,我希望git能够在不触及任何文件的情况下进行检出和合并(因为根据定义,没有文件会发生更改)。这不是什么大问题,只是一个小小的不便。 - Tomas Lundell

3

没有办法在不触及工作目录(和索引)的情况下进行合并(或变基),因为可能会出现必须使用工作目录(和/或索引)解决的合并冲突。

您可以始终拥有另一个克隆(可能使用替代方法或符号链接对象目录来节省磁盘空间),或者使用 contrib/workdir/git-new-workdir 等工具创建另一个工作目录。或者使用类似 ccache 的工具。

编辑:现在,git worktree 是 Git 核心的一部分,无需外部工具(至少从 git 版本 2.6.0 开始)。


不完全正确。Git 实际上可以处理多个工作树,因此您可以通过使用“工作树”而不必触及“工作目录”(您的主要检出)来完成此操作。无需额外的克隆或符号链接。请参见 https://dev59.com/u3A75IYBdhLWcg3wlqET#58630711。 - zanerock
@zanerock:在撰写此回复时,git-worktree 位于 contrib/ 目录下。我已相应地将其添加到答案中,感谢您的评论。 - Jakub Narębski

0

如果您只关心几个文件并且使用Unix,您可以手动更改最后修改时间 (mtime),在事后使用touch -d <timestamp>修复它们。确保使用 ls --full-time 来获取时间戳,因为默认显示缺乏精度。

例如,假设您正在使用Docker为基于Python的Web应用程序构建镜像。如果更改了requirements.txt文件,则需要很长时间才能重新构建,因为它必须下载一堆第三方库并编译它们。合并后简单地重置该文件的mtime即可:

ls -og --full-time src/requirements.txt
# -rw-r--r-- 1 282 2015-11-04 20:03:28.918979065 +0400 src/requirements.txt

git checkout master
git merge --no-ff feature-foo

touch src/requirements.txt -d "2015-11-04 20:03:28.918979065 +0400"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接