Git checkout and merge without touching working tree


Git checkout and merge without touching working tree

Say I have a feature branch, into which I merge upstream changes prior to pushing my changes back:
git branch feature1
… [edit my code]
… [commit]
git fetch origin master
git merge fetch_head [or rebase]
… [resolve conflicts]
… [build and test code]

At this point I wish to push my changes. The normal way of doing this would be:
git checkout master [changes a bunch of working tree files]
git merge feature1 [changes the same files right back]

This works fine, but will make the (date-checking) compiler think that a whole bunch of files are dirty and needs a rebuild even though the contents are the same. Is there a way to checkout-and-merge that leaves the working tree unchanged in this case?
Something like:
git checkout master –merge-branch feature1

I am only talking about fast forward merges that by definition would not change the state of the files.


Solution 1:

[Edit] This is only a partial solution / workaround. See the actual answer by @djpohly below.

Firstly, you can push from anywhere. Doesn’t matter what you have checked out, or whether the commits you want to push are in master.

git push REMOTE_REPO feature1:master

see git help push

Hint: git push remoteRepo localRef:remoteRef

As for bringing master to where you are now without fiddling with your working copy… You can force it like so:

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

But this does a hard reset on master. ie it doesn’t check for fast-forward.

Solution 2:

A simple and safe way to do this—without a push or a forced update—is to fetch feature1 into master:

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

The trick is using . to get the local feature1 ref. This is safer than forcibly updating the master branch, since it ensures the update is a fast-forward. (See the <refspec> parameter in the git-fetch documentation for details.)

Now that feature1 and master are the same, switching between them will not touch any files:

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

Solution 3:

There is no way that merge (or rebase) can work without touching the working directory (and index), as there can be merge conflicts that have to be resolved using working directory (and/or index).

You can always have another clone (perhaps using alternates, or symlinking objects directory, to save disk space), or another working directory with contrib/workdir/git-new-workdir. Or use a tool such as ccache.

Solution 4:

If you only care about a couple of files and you’re using Unix, you can change manually fix the mtime after the fact using touch -d <timestamp>. Make sure you use ls --full-time to get the timestamp, as the default display lacks precision.

For example, imagine you’re using Docker to build an image for a Python-based web app. If the requirements.txt file changes it takes a long time to rebuild, because it has to download a bunch of 3rd-party libraries and compile them. Simply reset that file’s mtime after merging:

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"