Paul M
Paul M

Reputation: 4157

Is it possible to ignore file changes in a merge?

I have two branches, Master and Exhibition. Exhibition is just a local branch.

When I run a diff on the two branches it shows me that sitemap.xml has a few differences between the branches, paths to local config. However when I merge from Exhibition into Master, the changes in Sitemap.xml don't merge across, which is good (as they contain local paths).

Doing a git status on both branches shows nothing to add/commit, unless I have made other changes, those changes I add, then commit and they go across in the merge.

The sitemap.xml file is not in gitignore.

At some point I must have set something up, so the sitemap changes are not committed to the live master version, but I don't know what, or how to find out what. Is there something in Git that allows this?

I need to recreate this behavior because someone else has made some changes in a new branch that overwrites my sitemap.xml with his changes. Am I missing something obvious? I would have thought had I not added or committed it, it would show up in the git status.

Upvotes: 1

Views: 163

Answers (2)

Mark Adelsberger
Mark Adelsberger

Reputation: 45659

It is unlikely that you have some setup telling git to ignore the sitemap in a merge; especially if it is merging sitemap changes from your coworker's branch. It's more likely that the "merge base" between your branches has the same version of sitemap.xml as your exhibition branch. In that case, during the merge the exhibition branch would not be seen as changing the file, while the master branch would be seen as changing the local paths you're talking about; so git would apply the master branch changes and you'd end up with the file as it already looks on master.

You could test this with a command like

git diff --name-only exhibition $(git merge-base exhibition master)

If the list this prints doesn't include the sitemap, then the above explains what you're seeing.

If that's not the case, then it's unclear what you'd have done to cause this behavior (and that's a bit of a problem, because depending on what you did, there could be other effects as well). In particular, you mention .gitignore; this has nothing to do with how merges are processed. The only thing .gitignore does is make it so that untracked files at specified paths, by default, stay untracked.

This type of question usually draws out at least one response suggesting the use of index flags like skip-worktree or assume-unchanged. These "solutions" might sometimes sort of work, but I don't recommend them. (For one thing, since the index isn't shared, they would only be effective in your local repo; so one possibility is that you set something like this up and are now running into one of its limitations.) At the very least, this is not the behavior they're designed to support.

There isn't really a good way to do this in git. You could come close by setting up a custom merge driver, but that only works when the file itself needs to be merged; so if the file isn't changed on master, this would have no effect.

The best solution for a file that needs to contain local paths or configurations is to store a template for the file under source control and generate the file itself during your build process. The generated file should not be put in source control in that case, so it would either be included in .gitignore or generated at a path outside the work tree (I prefer the latter, personally). You could then include one or more files in source control with values to be merged into the template under different circumstances, and use your build tooling to select either one of those or a non-source-controlled one with local values.

Upvotes: 1

Micha Wiedenmann
Micha Wiedenmann

Reputation: 20823

You probably marked sitemap.xml as assume unchanged. From git help update-index:

In order to set "assume unchanged" bit, use --assume-unchanged option. To unset, use --no-assume-unchanged. To see which files have the "assume unchanged" bit set, use git ls-files -v (see git-ls-files(1)).

The following example showcases the behavior

$ git init
Initialized empty Git repository in /tmp/foo/.git/
$ echo 1 > sitemap.xml; git add sitemap.xml; git commit -m "sitemap"
[master (root-commit) f1a1f79] sitemap
 1 file changed, 1 insertion(+)
 create mode 100644 sitemap.xml
$ git checkout -b exhibition
Switched to a new branch 'exhibition'

$ echo 2 > sitemap.xml
$ git update-index --assume-unchanged sitemap.xml
$ git status
On branch exhibition
nothing to commit, working tree clean

$ echo a > A; git add A; git commit -m a
[exhibition af58c3b] a
 1 file changed, 1 insertion(+)
 create mode 100644 A
$ git status
On branch exhibition
nothing to commit, working tree clean

References

--[no-]assume-unchanged

When this flag is specified, the object names recorded for the paths are not updated. Instead, this option sets/unsets the "assume unchanged" bit for the paths. When the "assume unchanged" bit is on, the user promises not to change the file and allows Git to assume that the working tree file matches what is recorded in the index. If you want to change the working tree file, you need to unset the bit to tell Git. This is sometimes helpful when working with a big project on a filesystem that has very slow lstat(2) system call (e.g. cifs).

Git will fail (gracefully) in case it needs to modify this file in the index e.g. when merging in a commit; thus, in case the assumed-untracked file is changed upstream, you will need to handle the situation manually.

Upvotes: 1

Related Questions