209

How can I safely update (pull) a git project, keeping specific files untouched, even if there's upstream changes?

myrepo/config/config.php

Is there a way, of, even if this file was being changed on remote, when I git pull, everything else is updated, but this file is unchanged (not even merged)?

PS. I need to do what I am asking because I'm only writing git-based deploy scripts. I cannot change config files to templates.

so, I need way to write update scripts that does not lose what was locally changed. I was hoping for something as simple as:

git assume-remote-unchanged file1
git assume-remote-unchanged file2

then git pull

Valerio Bozz
  • 1,176
  • 16
  • 32
Johnny Everson
  • 8,343
  • 7
  • 39
  • 75
  • Are the changes to `config.php` committed? – Mark Longair May 02 '12 at 13:58
  • 1
    possible duplicate of [Is it possible to exclude specific commits when doing a git merge?](http://stackoverflow.com/questions/332528/is-it-possible-to-exclude-specific-commits-when-doing-a-git-merge) – Daenyth May 02 '12 at 13:58
  • 1
    It's not committed. I would rather not have to – Johnny Everson May 02 '12 at 14:12
  • @JhonnyEverson: It's the same overall class of problem (You have a configuration file and you don't want to commit specific settings, but the configuration file structure needs to be tracked.) – Daenyth May 02 '12 at 16:10

7 Answers7

377

There is a simple solution based on Git stash. Stash everything that you've changed, pull all the new stuff, apply your stash.

git stash
git pull
git stash pop

On stash pop there may be conflicts. In the case you describe there would in fact be a conflict for config.php. But, resolving the conflict is easy because you know that what you put in the stash is what you want. So do this:

git checkout --theirs -- config.php
GoZoner
  • 67,920
  • 20
  • 95
  • 145
  • 7
    that git checkout --theirs command is very confusing. It did what I wanted once and something really bad another time. Got any good documentation on it? – Milimetric Sep 19 '13 at 15:00
  • 4
    Yes, sometimes 'theirs' and 'ours' can be confusing. In a `git merge` 'ours' is what is currently in the working directory. But for a `git rebase` (or `git rebase -onto`) the meaning can get swapped. – GoZoner Sep 19 '13 at 16:13
  • @GoZoner I think this works for fetch also. It is just that after `git stash` one has to checkout different branch and then `git fetch origin :`. then checkout the previous branch and then `git stash pop`. I tried and it worked but i didn't have any merge conflict. – ART Jun 12 '15 at 16:59
  • 2
    `git stash , git pull , git stash apply` You can drop the stash with : `git stash drop` Once you merge the changes successfully – BeingSuman Dec 07 '16 at 07:41
  • 32
    The thing with git is that you have to understand what it is going to do. The other thing with git, is that even for smart people it's often very difficult to understand what git is going to do. – Bradley Thomas Jan 06 '17 at 16:07
  • 4
    You should check if there actualy is anything to stash. Otherwise you would pop stashed changes from an earlier `git stash`. – Jörn Reimerdes Aug 30 '18 at 10:29
  • "git pull origin master " if you want that branch – Alberto Perez Dec 09 '19 at 10:47
  • simpler than git pull -X ours ? – Peter Cotton Jul 29 '21 at 16:52
  • 1
    @Alberto Or `git pull origin main` for those who've made that change (I might have stragglers in my repositories). – Alonso del Arte Oct 30 '21 at 20:18
  • Thank you, this did it for me without having to use `git checkout`. – Alonso del Arte Oct 30 '21 at 20:19
19

I use

git pull -X ours

which will instruct git to use the local version when merging.

Peter Cotton
  • 1,671
  • 14
  • 17
  • Note that the question was improved to clarify that, it was about "uncommitted changes". So, this answer with uncommitted changes will cause: error: cannot pull with rebase: Your index contains uncommitted changes. error: please commit or stash them. – Valerio Bozz Apr 17 '23 at 08:28
  • Very good point. – Peter Cotton Apr 18 '23 at 14:03
17

If you have a file in your repo that it is supposed to be customized by most pullers, then rename the file to something like config.php.template and add config.php to your .gitignore.

KurzedMetal
  • 12,540
  • 6
  • 39
  • 65
  • 6
    check [this answer](http://stackoverflow.com/a/3970442/236871) too, you can use `.gitattributes` to always keep your changes while merging. – KurzedMetal May 02 '12 at 13:43
  • That's more like of what I needed. The template thing is the elegant solution but I am afraid I cannot use this since I am just writing deploy scripts. – Johnny Everson May 02 '12 at 13:47
7

Update: this literally answers the question asked, but I think KurzedMetal's answer is really what you want.

Assuming that:

  1. You're on the branch master
  2. The upstream branch is master in origin
  3. You have no uncommitted changes

.... you could do:

# Do a pull as usual, but don't commit the result:
git pull --no-commit

# Overwrite config/config.php with the version that was there before the merge
# and also stage that version:
git checkout HEAD config/config.php

# Create the commit:
git commit -F .git/MERGE_MSG

You could create an alias for that if you need to do it frequently. Note that if you have uncommitted changes to config/config.php, this would throw them away.

Community
  • 1
  • 1
Mark Longair
  • 446,582
  • 72
  • 411
  • 327
7

In case there are local uncommitted changes, to avoid merge conflict while pulling:

git stash save
git pull
git stash pop

Source:

https://git-scm.com/docs/git-stash

Valerio Bozz
  • 1,176
  • 16
  • 32
Ujjwal Roy
  • 500
  • 6
  • 9
6

We can also try git pull with rebase

git pull --rebase origin dev
  • Note that the question was improved to clarify that, it was about "uncommitted changes". So, this answer with uncommitted changes will cause: error: cannot pull with rebase: Your index contains uncommitted changes. error: please commit or stash them. – Valerio Bozz Apr 17 '23 at 08:29
2

To answer the question : if you want to exclude certain files of a checkout, you can use sparse-checkout

  1. In .git/info/sparse-checkout, define what you want to keep. Here, we want all (*) but (note the exclamation mark) config.php :

    /* !/config.php

  2. Tell git you want to take sparse-checkout into account

    git config core.sparseCheckout true

  3. If you already have got this file locally, do what git does on a sparse checkout (tell it it must exclude this file by setting the "skip-worktree" flag on it)

    git update-index --skip-worktree config.php

  4. Enjoy a repository where your config.php file is yours - whatever changes are on the repository.


Please note that configuration values SHOULDN'T be in source control :

  • It is a potential security breach
  • It causes problems like this one for deployment

This means you MUST exclude them (put them in .gitignore before first commit), and create the appropriate file on each instance where you checkout your app (by copying and adapting a "template" file)

Note that, once a file is taken in charge by git, .gitignore won't have any effect.

Given that, once the file is under source control, you only have two choices () :

  • rebase all your history to remove the file (with git filter-branch)

  • create a commit that removes the file. It is like fighting a loosing battle, but, well, sometimes you have to live with that.

Pierre-Olivier Vares
  • 1,687
  • 15
  • 20
  • this works perfectly! I quote the manual: `git read-tree and other merge-based commands (git merge, git checkout…​) can help maintaining the skip-worktree bitmap and working directory update. $GIT_DIR/info/sparse-checkout is used to define the skip-worktree reference bitmap. When git read-tree needs to update the working directory, it resets the skip-worktree bit in the index based on this file, which uses the same syntax as .gitignore files. If an entry matches a pattern in this file, skip-worktree will not be set on that entry. Otherwise, skip-worktree will be set.` – Onno Rouast Nov 17 '20 at 15:54