Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps

Git – Problem with “skip-worktree” and Pull

5.00/5 (3 votes)
10 Jul 2023CPOL4 min read 7.1K  
Using Git “skip-worktree” option can create problems with Git Pull
Using Git “skip-worktree” option is the recommended approach when you want files TRACKED by Git, but want LOCAL CHANGES IGNORED, and is more appropriate than .gitignore. However, if files are modified by other users, Git Pull can be problematic and requires elaborate handling.

1. Problem

If you have a config file like web.config that needs to be TRACKED by Git, but want LOCAL CHANGES IGNORED, that situation cannot be solved with .gitignore. We show alternative ways to achieve that.

For more discussion on methods to ignore files in Git, see [1].

The skip-worktree option is a less-known Git option. This method is planned to be applied to files that you want TRACKED, LOCAL CHANGES IGNORED. This is the recommended alternative to .gitignore method.

But, the problem is, the option skip-worktree still requires careful management of the situation in Git. If the original file is modified elsewhere, an attempt to execute Git Pull can lead to an error and to the Pull being refused. Careful manual management/resolution of the situation is needed.

2. Usage of skip-worktree from the Command Line

Here are the commands you will need for this method:

To ignore local changes to tracked files:

git update-index --skip-worktree  [<file>...]

To track local changes again:

git update-index --no-skip-worktree [<file>...]

To list all files marked with skip-worktree:

git ls-files -v | grep ^S

3. Problematic Situation with Option “skip-worktree”

Let us explain how a problematic situation is generated. Let us assume we have two users, each with its own repository. Let us focus on some file config.txt. Initially, all repos have the same version of the file.

Image 1

Then, User1 on his system decides to modify the config.txt but does not want changes committed, so it marks it with the option skip-worktree.

Image 2

Then, User2 on his system modifies config.txt as a regular part of his work.

Image 3

Then, User2 commits his work and Pushes changes to the Remote Repo.

Image 4

Now, we have a problem when User1 wants to Pull from Remote Repo.

Image 5

The problem is that Pull is refused with error. File config.txt cannot be modified. Even worse, we have now two versions of file config.txt, version A1 and A2.

The error generated by Pull will be similar to:

error:
Your local changes to the following files would be overwritten by merge:
                Folder1/config.txt
Please commit your changes or stash them before you merge.
Aborting

4. Understanding the Problem

The problem is that on “Repo Local 1”, we have config.txt version A1, which is modified and protected by the flag skip-worktree. Git cannot touch that file. User1 wanted to have the local version A1 of config.txt, but now the global version has changed to A2.

If the user just releases a flag skip-worktree (git update-index --no-skip-worktree config.txt), file config.txt Ver A1 will just appear UNSTAGED and UNCOMMITTED in “Repo Local 1”.

User1 now definitely needs to merge versions A1 and A2, just the question is if he wants to commit changes from his version A1 to the “Remote Repo”.

5. Problem Resolution

5.1. Variant 1: Commit local changes

If User1 decides he actually wants to commit his changes ver A1 to “Remote Repo”, he releases the flag skip-worktree. Since file config.txt ver A1 is not committed, Git Pull will still not work. User1 decides to commit the file config.txt to “Repo Local 1”. User1 does Pull again, then automatically or manually merges config.txt into version A12. Then it commits file A12 into “Repo Local 1”. And then does Push to the “Remote Repo”.

Now “Remote Repo” has version A12, and User1 also in “Repo Local 1” has version A12.

Image 6

Steps for User1 are:

  1. Release flag skip-worktree on config.txt ver A1 in “Repo Local 1”
    (git update-index --no-skip-worktree config.txt)
  2. Commit file config.txt ver A1 to “Repo Local 1”
  3. Pull
  4. Resolve file merge (resulting in file config.txt ver A12)
  5. Push (file config.txt ver A12 to “Remote Repo”)

5.2. Variant 2: Stash Local Changes

If User1 decides he does not want to commit his changes to “Remote Repo”, he releases the flag skip-worktree. Since file config.txt ver A1 is not committed, Git Pull will still not work. User1 decides to stash the file config.txt. User1 does Pull again and gets into “Repo Local 1” file config.txt ver A2. Then User1 applies file from stash to “Local Repo 1”. Git will do an automatic or manual merge. User1 manually merges the new file into version A12. Then, User1 applies flag skip-worktree to file A12.

Now “Remote Repo” has version A2, and User1 in “Repo Local 1” has version A12.

Image 7

Steps for User1 are:

  1. Release flag skip-worktree on config.txt ver A1 in “Repo Local 1”
    (git update-index --no-skip-worktree config.txt)
  2. Stash file config.txt ver A1
  3. Pull
  4. Apply stash to “Repo Local 1”
  5. Resolve file merge (resulting in file config.txt ver A12)
  6. Set flag skip-worktree on config.txt ver A12 in “Repo Local 1”
    (git update-index --skip-worktree config.txt)

6. Conclusion

The Git option skip-worktree is the recommended way of dealing with situations when you have config files like web.config that you need to locally change. But, sometimes situations can become quite complicated, like in the above problem when the same file gets modified elsewhere and then the Git Pull is not working.

Nothing is easy with Git. I use the popular Git GUI SourceTree, but still, without a good understanding of what are you doing, the tool itself will not solve the problem.

7. References

8. History

  • 10th July, 2023: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)