简体   繁体   中英

Skipping work-tree in git for glob patterns

My remote repo has .log files that are getting used for code execution. Hence they need to be tracked by Git. While other developers work on this repo, they push their changes related to log files. When I pull those changes, it creates conflicts.

For temporarily ignoring these tracked files, I have tried the below option:

git ls-files *.log* -z | xargs -0 git update-index --skip-worktree

However, this will only skip worktree for log files that are currently present. If there are new log files added into tracking by other developers, I will need to re-run this command. Is there a way to configure git to skip the worktree for all files having *.log* glob pattern?

No (but see footnote 2 at the bottom, which is an answer to a question you did not quite ask). The --skip-worktree option sets a flag bit on an existing index entry. New index entries never have either or the assume-unchanged or skip-worktree bits set.

New index entries will be created when:

  • you use git add to copy a file into Git's index, when the file was not there before; or
  • you use git checkout or git switch , or one of Git's other index-manipulation commands, to copy a file into Git's index that was not there before.

If there are new log files added into tracking by other developers ...

It's important to realize that unless you share the repository itself —which is generally a bad idea—other developers are not changing which files are tracked , because the concept of tracked vs untracked, for some work-tree file, is based on your repository and work-tree.

That is:

  • Git is about commits . Users make new commits and add them to their Git repository, and then you have your Git repository connect to their Git repository (or to some central sharing site to which they have sent their commits) and get the commits from them. (That is, they might run git push in their repository, which would send their commits to the central sharing site. Then you run git fetch origin to get those commits into your own repository. If you're using git pull , note that git pull first runs git fetch , then runs a second Git command to do something with the fetched commits.)

  • While commits contain files, the files inside a commit are in a read-only, compressed and de-duplicated format that only Git itself can read. These files are frozen for all time. They are not useful on their own. (But they are de-duplicated, so that if hundreds of commits have the same data in some file—which is often the case because every commit has every file in it—there's really only one copy of the file, shared by all those commits.)

  • So, when you git checkout a specific commit, your Git copies the files from the given commit.

So the main idea here is that when you are working with some commit, there are two copies of the files from that commit. One, the frozen Git-only copy, is in the current commit , and the other, which you can read and write and generally work with, is in your working tree or work-tree . Here, Git is like most version control systems: they all have to do something like this, since there's a committed copy that literally can't change, and a usable copy that can.

But here, Git departs from other version control systems by keeping a third copy, in between the frozen committed copy and your usable work-tree copy. This third copy is actually not really a copy because it's in Git's frozen format, and already de-duplicated. What makes it different from the committed copy is that it is not in a commit: it's in Git's index . This means you can replace it wholesale, by substituting in a new de-duplicated copy of some other file.

That's what git add does: it creates an all-new index entry, to hold the file name and new, but already de-duplicated (Git blob object ), file content; or it replaces the existing entry's content (the internal blob object hash ID), while keeping the file name and flags from the existing index entry. 1 This is where your --skip-worktree flag comes in.

The index, which Git also calls the staging area , only contains entries for files that will go into the next commit. These are the files that Git knows about. They're already in Git's index, right now . The index holds the file's name and mode, a de-duplicated internal Git blob object hash ID, and a bunch of other cache information Git finds useful. That includes the skip-worktree bit.

But because the index represents the next commit you will make , it contains only those files that will be in the next commit. If a file doesn't exist, it can't be in the next commit—so it isn't in the index and therefore there is no skip-worktree bit for it.

Similarly, because the index represents the next commit you will make, when you use git checkout to select a commit to work on / with, Git will fill its index from that commit, so that the next commit is similar. Suppose someone has created a new log file, that has never existed before, in a new commit. As soon as you have Git check out this commit, Git will need to add the log file to its index. This will be a new entry and won't have the skip-worktree bit set.

This also brings us to the definition of an untracked file , and hence by contrast, a tracked file. An untracked file is any file that is in your work-tree right now that is not in Git's index right now . A tracked file is therefore one that is in Git's index right now . That's all there is to it, but:

  • Your work-tree is under your control: you can create and remove files any time.
  • Git's index is under Git's control. Checking out a commit fills it. But you have some control too: git add copies into it, and git rm removes from both Git's index and your work-tree.

Since you can only set the assume-unchanged and skip-worktree flags on index entries, you can only set them on files that are (currently) tracked. The set of tracked files changes, at least potentially, as you switch from one commit to another. If you want to set particular bits on particular index entries, you have to do that as you switch from one commit to another. 2


1 Because git add means make the index copy match the work-tree copy , git add can also remove from the index any file that is no longer present in your work-tree. With the skip-worktree bit set, though, git add won't make the index copy match the work-tree copy: it won't copy an updated file back into Git's index, and it won't remove, from Git's index, any file that is missing in your work-tree. This second part relates directly to footnote 2.

2 This isn't entirely true: Git supports something called sparse checkouts , where Git will deliberately set the skip-worktree bit on some new index entries. This is in fact the original intent for the skip-worktree bit. However, the way this works is that you list, for Git, the files that should appear in your work-tree. Git will then make sure not to copy committed (and now indexed) files into your work-tree if they're marked "do not check out due to sparse checkouts". That is, git checkout will see that this file should not be checked out, and will instead set the skip-worktree bit.

The problem here is that of course these files also won't appear in your work-tree. That is, these *.log* files won't be checked out at all. They will be in Git's index, and therefore will be in new commits that you make, but you won't be able to look at the files. This might be OK for your particular purpose. In that case, stop setting the bit manually, and look into sparse checkouts.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM