26

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

EDIT:

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

4 Answers4

44

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'
(master)$
djpohly
  • 711
  • 5
  • 8
  • Thanks, this worked. Although, I don't understand what the `fetch` part is doing. – towi Dec 12 '12 at 11:05
  • 1
    @towi You're effectively doing a `git pull` from yourself without updating the working copy (git pull is basically a combination of `git fetch` and `git merge`). – dlitz Nov 01 '13 at 18:05
  • 4
    This should be the accepted answer. I keep coming back to it in various scenarios :) – Andrew Mao Jul 29 '14 at 21:46
9

[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.

JasonWoof
  • 4,176
  • 1
  • 19
  • 28
  • That's true, and fixes half of the problem. Even so, I can't really remain on the feature branch after it's completed. Since I'd pretty much would like to go back to master I would still have this (rather minor) problem. –  Sep 10 '09 at 02:47
  • 1
    well, the other part of your question (about wanting the modification times of your files somehow magically changed to the right thing) isn't really solvable. There's no way for git to know which files were compiled when. So git always updates timestamps when it changes files. This results in some extra re-compiling, but that's much better than it failing to re-compile when it should sometimes. – JasonWoof Sep 10 '09 at 03:40
  • 2
    maybe you'd like to have another clone on your computer, so you can have both checked out at once? If you care about space, it's possible to have two local clones share repo storage. – JasonWoof Sep 10 '09 at 03:42
  • 2
    I guess I was hoping that for fast forward merges git would be able to checkout-and-merge without touching any of the files (since none of them would change by definition). It's not a big deal, just a minor inconvenience. –  Sep 11 '09 at 01:23
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.

Edit: nowadays git worktree is a part of core Git, no need for external tools (at least since git version 2.6.0).

Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
  • Not quite. Git can actually handling multiple working trees, so you can do this without touching "the working directory" (your primary checkout), by working "a working tree". No additional clones or symlinks required. See https://stackoverflow.com/a/58630711/929494 – zanerock Oct 30 '19 at 17:27
  • @zanerock : at the time of writing this response git-worktree was in contrib/. I have added to the answer accordingly, thanks for the comment. – Jakub Narębski Oct 31 '19 at 15:07
0

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"
z0r
  • 8,185
  • 4
  • 64
  • 83