OJFord
OJFord

Reputation: 11130

git stash tracked but new files

$ git init
$ git commit -m 'initial commit needed to stash' --allow-empty
$ touch test
$ git add --intent-to-add test  # aka -N
$ git stash
error: Entry 'test' not uptodate. Cannot merge.
Cannot save the current worktree state

It's not untracked, so --include-untracked has no impact. Is there any way to stash an added but never committed file?

Upvotes: 1

Views: 405

Answers (1)

torek
torek

Reputation: 487775

Is there any way to stash an added but never committed file?

Only if it's truly added, i.e., there's an actual version in the index. Then git stash will work.

There have, in the past, been some bugs in Git around the --intent-to-add flag. Supposedly they are all fixed now, making it safe to use, but I'd recommend avoiding it unless it's doing something particularly important and/or useful for you today. What it does internally is create an index entity with a flag set, and each Git command that deals with the index is supposed to have special handling for this entity if / as needed:

$ git add --intent-to-add test
$ git ls-files --stage --debug
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0   test
  ctime: 0:0
  mtime: 0:0
  dev: 0    ino: 0
  uid: 0    gid: 0
  size: 0   flags: 20004000

The hash ID here, e69de29bb2d1d6434b8b29ae775ad8c2e48c5391, is the hash of an empty file:

$ git hash-object -t blob --stdin < /dev/null
e69de29bb2d1d6434b8b29ae775ad8c2e48c5391

The flags: 20004000 shows the CE_INTENT_TO_ADD flag from cache.h:

/*
 * Extended on-disk flags
 */
#define CE_INTENT_TO_ADD     (1 << 29)
#define CE_SKIP_WORKTREE     (1 << 30)

What git stash does is to make two (or sometimes three) commits that are not on any branch, then—in effect—run git reset --hard to set your index and work-tree back to the state they would have if you hadn't started working with the index and work-tree.1 The two commits hold the index (staging-area) state, as an ordinary commit / snapshot, and your work-tree state, as another commit / snapshot. The third commit, if it exists, holds any untracked files, possibly including ignored files. None of these commits have any room for these extended flags shown by git ls-files --debug, so the CE_INTENT_TO_ADD flag simply cannot be preserved.

Perhaps git stash could try to handle the flag by pretending it's not set at all (so that it writes an empty test file instead, then removes the index entry entirely). This would be mostly consistent: the file is, after all, tracked as an empty file. It just has this special "intent to add" status, plus the all-zero cache information since it doesn't exist as a file-system file. You would lose the special "intent to add" status in the process, of course. So the end result would be the same as if you'd just added empty files instead.


1This used to be exactly what it did, but then git stash got fancied up to allow for pathspec arguments. For several Git versions afterward, this kind of git stash could occasionally lose data. All the bugs here have been fixed—I think—but in general I don't recommend doing this kind of pathspec-based stash. In fact, I recommend avoiding git stash entirely except for some very short-term purposes. Stashes are, after all, just commits that don't have a lot of nice ways to find them again later. Make real commits, which do have good ways to find them again later.

Upvotes: 2

Related Questions