Note: fallengamer did some tests in 2011 (so they may be outdated), and here are his findings:
Operations
- File is changed both in local repository and upstream
git pull
:
Git preserves local changes anyway.
Thus you wouldn’t accidentally lose any data that you marked with any of the flags.
- File with
assume-unchanged
flag: Git wouldn’t overwrite local file. Instead it would output conflicts and advices how to resolve them
- File with
skip-worktree
flag: Git wouldn’t overwrite local file. Instead it would output conflicts and advices how to resolve them
- File is changed both in local repository and upstream, trying to pull anyway
git stash
git pull
Using skip-worktree
results in some extra manual work but at least you wouldn’t lose any data if you had any local changes.
- File with
assume-unchanged
flag: Discards all local changes without any possibility to restore them. The effect is like ‘git reset --hard
’. ‘git pull
’ call will succeed
- File with
skip-worktree
flag: Stash wouldn’t work on skip-worktree
files. ‘git pull
’ will fail with the same error as above. Developer is forced to manually reset skip-worktree
flag to be able to stash and complete the failing pull
.
- No local changes, upstream file changed
git pull
Both flags wouldn’t prevent you from getting upstream changes. Git detects that you broke assume-unchanged
promise and choses to reflect the reality by resetting the flag.
- File with
assume-unchanged
flag: Content is updated, flag is lost.
‘git ls-files -v
’ would show that flag is modified to H
(from h
).
- File with
skip-worktree
flag: Content is updated, flag is preserved.
‘git ls-files -v
' would show the same S
flag as before the pull
.
- With local file changed
git reset --hard
Git doesn’t touch skip-worktree
file and reflects reality (the file promised to be unchanged actually was changed) for assume-unchanged
file.
- File with
assume-unchanged
flag: File content is reverted. Flag is reset to H
(from h
).
- File with
skip-worktree
flag: File content is intact. Flag remains the same.
He adds the following analysis:
It looks like skip-worktree
is trying very hard to preserve your local data. But it doesn’t prevent you to get upstream changes if it is safe. Plus git doesn’t reset the flag on pull
.
But ignoring the ‘reset --hard
' command could become a nasty surprise for a developer.
Assume-unchanged
flag could be lost on the pull
operation and the local changes inside such files doesn’t seem to be important to git.
See:
He concludes:
Actually neither of the flags is intuitive enough.
assume-unchanged
assumes that a developer shouldn’t change a file. If a file was changed – then that change is not important. This flag is meant for improving performance for not-changing folders like SDKs.
But if the promise is broken and a file is actually changed, git reverts the flag to reflect the reality. Probably it’s ok to have some inconsistent flags in generally not-meant-to-be-changed folders.
On the other hand skip-worktree
is useful when you instruct git not to touch a specific file ever. That is useful for an already tracked config file.
Upstream main repository hosts some production-ready config but you would like to change some settings in the config to be able to do some local testing. And you don’t want to accidentally check the changes in such file to affect the production config. In that case skip-worktree
makes perfect scene.
With Git 2.25.1 (Feb. 2020), the "Actually neither of the flags is intuitive enough" mentioned above is further clarified:
See commit 7a2dc95, commit 1b13e90 (22 Jan 2020) by brian m. carlson (bk2204
).
(Merged by Junio C Hamano -- gitster
-- in commit 53a8329, 30 Jan 2020)
(Git Mailing list)
doc
: dissuade users from trying to ignore tracked files
Signed-off-by: Jeff King
Signed-off-by: brian m. carlson
It is quite common for users to want to ignore the changes to a file that Git tracks.
Common scenarios for this case are IDE settings and configuration files, which should generally not be tracked and possibly generated from tracked files using a templating mechanism.
However, users learn about the assume-unchanged and skip-worktree bits and try to use them to do this anyway.
This is problematic, because when these bits are set, many operations behave as the user expects, but they usually do not help when git checkout
needs to replace a file.
There is no sensible behavior in this case, because sometimes the data is precious, such as certain configuration files, and sometimes it is irrelevant data that the user would be happy to discard.
Since this is not a supported configuration and users are prone to misuse the existing features for unintended purposes, causing general sadness and confusion, let's document the existing behavior and the pitfalls in the documentation for git update-index
so that users know they should explore alternate solutions.
In addition, let's provide a recommended solution to dealing with the common case of configuration files, since there are well-known approaches used successfully in many environments.
The git update-index
man page now includes:
Users often try to use the assume-unchanged
and skip-worktree
bits to tell Git to ignore changes to files that are tracked.
This does not work as expected, since Git may still check working tree files against the index when performing certain operations.
In general, Git does not provide a way to ignore changes to tracked files, so alternate solutions are recommended.
For example, if the file you want to change is some sort of config file, the repository can include a sample config file that can then be copied into the ignored name and modified.
The repository can even include a script to treat the sample file as a template, modifying and copying it automatically.
That last part is what I describe a typical content filter driver based on smudge/clean scripts.
git update-index --skip-worktree
, and notgit sparse-checkout
or core.sparseCheckout=true. As such, these users would be unaffected by this change and can continue abusing the system as before." – Hypocaust