-1

While playing with branches on git I've noticed some unobvious behaviour:

Let's say I have a following structure in my repo:

master---
         \---feature_branch---
                              \---test_branch

I'm having working tree clean. Now I add a file on the test_branch:

git checkout test_branch
touch test_file.txt

If I run git status now, I'm getting the following:

On branch test_branch
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    test_file.txt

nothing added to commit but untracked files present (use "git add" to track)

What bothers me is that I receive the same status whatever the branch I'm on.

This is not the case when I handle a committed file (seen below), because git prohibits me to change a branch without committing:

master---
         \---feature_branch---
                              \---test_branch

Having working tree clean, I run the following:

git checkout test_branch
touch test_file_2.txt
git add .
git commit -m "Add a file"
echo "foobar" >> test_file_2.txt

When I run git status now, I'll get:

On branch test_branch
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   test_file_2.txt

no changes added to commit (use "git add" and/or "git commit -a")

When I run git checkout now - I'll get:

error: Your local changes to the following files would be overwritten by checkout:
    test_file_2.txt
Please commit your changes or stash them before you switch branches.
Aborting

So I'm being able to switch branches when having an untracked file. I can add it to index even from master, because it's the branch I'll be committing from will decide the file's fate. However, I cannot switch the branch when having a file already committed.

What is the reason for this behaviour?

tomdonarski
  • 49
  • 2
  • 8
  • 2
    *Untracked* files are not in Git's index at all and therefore Git doesn't manage them. Files that are committed *are* in Git's index, at least at the moment because of the current commit and because you have not taken that file *out* of the index. Related: https://stackoverflow.com/q/22053757/1256452 – torek Jan 29 '20 at 20:38
  • 1
    The problem is not that the file is tracked, the problem is that the file is different *in the repository* between those two branches. So you're on branch A, where in the repository there is a copy of the file. Then you modify the file, and you try to switch to branch B, but on branch B, the file is different from what it is on branch A, which means git wants to update that file. However, since you have uncommitted changes to that file already, git doesn't want to risk modifying the file and causing problems, losing your changes, so it prevents the switch. – Lasse V. Karlsen Jan 29 '20 at 20:45
  • 1
    If you had two branches with the file present, but the file has the same content on both branches, then you would be allowed to switch between the branches even with uncommitted changes. But since the file is different, you're not. – Lasse V. Karlsen Jan 29 '20 at 20:46

1 Answers1

0

What bothers me is that I receive the same status whatever the branch I'm on.

For purposes of illustration, let's suppose there is just one file in the workspace. All you've done is git init, create the file, add it, and commit.

themini:gittest mattneubelcap$ git init
Initialized empty Git repository in /Users/mattneubelcap/gittest/.git/
themini:gittest mattneubelcap$ echo "test" > test.txt
themini:gittest mattneubelcap$ git add .
themini:gittest mattneubelcap$ git commit -m "test"
[master (root-commit) 1569fd7] test
 1 file changed, 1 insertion(+)
 create mode 100644 test.txt

You are on branch master and you make branch mybranch without checking it out. Now you change the first file. You also create another file. And you ask about the status.

themini:gittest mattneubelcap$ git branch mybranch
themini:gittest mattneubelcap$ echo "test still" > test.txt
themini:gittest mattneubelcap$ echo "test2" > test2.txt
themini:gittest mattneubelcap$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   test.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    test2.txt

no changes added to commit (use "git add" and/or "git commit -a")

The two files have different statuses. test.txt is modified, but test2.txt is untracked.

Now let's change test2.txt and look at the status again.

themini:gittest mattneubelcap$ echo "test2 still" > test2.txt
themini:gittest mattneubelcap$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   test.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    test2.txt

no changes added to commit (use "git add" and/or "git commit -a")

Observe that git does not remark on the fact that we have changed test2.txt. It is untracked! All git knows is: hey, there's a file sitting in your work folder, and you haven't told me what to do with it.

Okay, we add test.txt (because git told us we've modified it) and commit, and switch branches.

themini:gittest mattneubelcap$ git add test.txt
themini:gittest mattneubelcap$ git commit -m "changed test.txt"
[master 316c8b7] changed test.txt
 1 file changed, 1 insertion(+), 1 deletion(-)
themini:gittest mattneubelcap$ git checkout mybranch
Switched to branch 'mybranch'
themini:gittest mattneubelcap$ cat test.txt
test

test.txt is still in its old form here, because we branched before we changed the text from "test" to "test still". What's our status?

themini:gittest mattneubelcap$ git status
On branch mybranch
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    test2.txt

nothing added to commit but untracked files present (use "git add" to track)

No word about test.txt; it is there, but there is nothing interesting to know about it. On the other hand, test2.txt is still an anomaly. git won't touch it, but it will continue to complain that it is mystified about this file.

Let's switch back.

themini:gittest mattneubelcap$ git checkout master
Switched to branch 'master'
themini:gittest mattneubelcap$ cat test.txt
test still
themini:gittest mattneubelcap$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    test2.txt

nothing added to commit but untracked files present (use "git add" to track)

There's the other version of test.txt. Our status still says nothing about test.txt, though, because there is nothing to know about it. But test2.txt remains an anomaly and git tells us about it.

Do you see what this shows? For all files that git is tracking, when you switch branches, you get the version of that file belonging to that branch. But for all files that git is not tracking, nothing happens; the file just sits there while the world changes around it. That's because the file is not tracked. It is no business of git's to make it come and go. It is no business of git's to know (and it does not know) whether you have modified it since creating it. And git will continue to mention that the file exists and is untracked, until you either:

(a) add it (thus telling git to start tracking it); or

(b) remove it; or

(c) tell git to ignore it.

matt
  • 515,959
  • 87
  • 875
  • 1,141