Home > Back-end >  switching branch in visual studio keeps the files form older branch
switching branch in visual studio keeps the files form older branch

Time:03-18

i created a empty repo and cloned the main branch

then i created an empty asp.net core api project and checked it in main branch

then i created new branch (services) and from main branch and added another project in solution (class library project) and named it "services"

this creates a folder called "services"

then i staged it , committed staged and pushed it in "services" branch

now when i checkout main branch i expect the "services" folder to be not present in my file system. but it remains there.

am i do something wrong or that's how it works ?

CodePudding user response:

Git doesn't "do" folders. Git only understands files.

Files have names like path/to/file.ext, in Git. That's not a folder named path containing a folder named to containing a file named file.ext: it's a file whose name is path/to/file.ext, complete with forward slashes (even if your OS uses backwards ones as in path\to\file.ext).

Your OS, however, can't handle this. So Git accommodates your OS by, when it needs to, creating folders in the file system, to hold files whose names have slashes in them. If you also create such folders:

mkdir path path/to

and then create files within these folders and git add them:

echo new stuff > path/to/file.ext
git add path/to/file.ext

Git understands that you mean to add to Git a file with the slashes in the name, and does so. It doesn't pay any attention to the folders—it doesn't add a path/ or a path/to/, just the final path/to/file.ext—but it knows that your OS can't work without them, so it accommodates them.

When you then switch back to some other commit, Git will sometimes clean out unnecessary folders. There are several conditions required before Git can do this, and one of them is that the folder be empty after Git removes any files that go with the commit you're moving away from and installs any files that go with the commit you're moving to. (See the sidebar below.)

If Git didn't remove the folder on its own, and git clean -df doesn't remove it either, the folder is non-empty: it contains some file that either isn't in Git at all, or goes with the commit you switched to. Note that these files may be hidden: always use the "show all files, including hidden ones" in whatever you are using to view the contents of the folder. Be careful with git clean! See the sidebar below.

Sidebar (long and optional): Commits hold snapshots

Every Git commit contains two things:

  • It has some metadata, or information about the commit itself, including things like the name and email address of the person who made the commit, some date-and-time stamps, a log message, and so on. We'll ignore that here, although one part of the metadata is absolutely crucial for Git's own internal operations: without it, there would be no history in the repository.

  • Besides the metadata, each commit acts as a snapshot of all files that Git knew about at the time you (or whoever) made the commit. This snapshot is much like a tarball or WinRar archive of every file. However, the files in this snapshot are in a special, compressed, read-only, Git-only format in which the contents are de-duplicated.

That means that if you have a million commits, each of which holds a million-byte file, but all 1 million of those files match, there's really only one million-byte file. Each snapshot is using the same file over and over again. Only if you change and git add the file do you get a new and different snapshot (which can be re-used for the next million commits, if it doesn't change again).

(While it doesn't happen immediately, Git eventually "super-compresses" files, if it can, so that if the two million-byte copies can be compressed against each other, one of them might shrink down to just a few dozen bytes in the end. This depends on delta encoding, which works pretty well for some text formats, but hardly ever works for pre-compressed binary data like JPG images.)

The snapshot inside each commit is completely, totally, 100% read-only. (In fact, so is the metadata.) This is why different commits can share files within their snapshots: since none of the commits is allowed to change any of them, all commits are allowed to share any of them. But the fact that these files are only readable, and even then, only by Git itself, means that you literally cannot do any work with these files.

Therefore, you don't. You never work with the files in the commit. Instead, you work with copies of the files, copied out of the commit into a working area. In other words, you don't work with files that are in Git, you work with files that aren't in Git. The files that are in Git aren't the ones you use, and the ones you use aren't in Git.

It's only when you run git add and git commit that Git makes a new snapshot. The new snapshot consists of:

  • all the previously extracted snapshot's files,
  • minus any you git rm-ed,
  • plus any you git add-ed

Git keeps a sort of running proposed next commit, filled in initially by git checkout or git switch when you switch to some commit. When you git add some existing file, Git adjusts the proposed next commit by booting out the current copy and putting in a new copy: Git compresses the working tree copy—the one that's not in Git—down into an internal, read-only form, checks for duplicates, and if there's a duplicate, uses that now. If not, Git prepares the compressed file for committing and uses that. In either case the proposed next commit is now updated.

When you git rm a file, Git removes the working tree copy (the one you can see and work with) and the proposed-next-commit copy. The proposed next commit is now updated.

When you git add a totally new file, Git compresses the working tree copy, checks to see if it's a duplicate (as for git add-ing an existing file), and then either adds the duplicate or the prepared file to the proposed next commit. The proposed next commit is now updated.

Any time you switch commits, Git:

  • Checks over the current "proposed next commit": are all the files in it, exactly the same as the current commit? If so, they're safe to discard. If not, they aren't.

  • Checks over the commit you'd like to switch to. Does it have exactly the same files that are in the current commit right now? If so, Git doesn't have to fiddle with those, in either the proposed next commit or your working tree, so it won't bother. If not, Git will have to add or remove some file(s) to or from the proposed next commit. Are the ones it's going to touch here safe to discard?

All this checking is what gives rise to the complicated answer to Checkout another branch when there are uncommitted changes on the current branch. There are two very important short-cut notions we can use here though:

  • If we're asking Git to "switch" from commit a123456, say, to commit a123456—i.e., if the hash ID of the current commit exactly matches the hash ID of the new commit we'd like to switch to—then we're not switching commits at all. That means there's no need to update the proposed next commit at all, and that in turn means the branch-switching is allowed. So we can always create and switch to a new branch if we don't change commit hash IDs in the process. That's always the case for a simple:

    git switch -c new-branch
    

    or

    git checkout -b new-branch
    

    that creates and switches to a new branch without changing commits. So that means if we started work and forgot to create a new branch first, we just do that before we run git commit and we're fine.

  • If all files are "clean"—if we have not made any changes since we last made a new commit, for instance, so that git status says nothing to commit, working tree clean—then we can switch branches safely, regardless of which commit we're moving from and to.

There is, however, one very big caveat to the second bullet point. Git is going to remove, from our working tree and from its index / staging-area—i.e., from the proposed next commit—all the files that are there because of the current commit and is going to install, in our working tree, all the files that go with the other commit. This is true even if some files are marked as "ignored" with a .gitignore: a file that's actually in the staging area is "tracked", by definition, and is thus subject to removal-and-replacement (provided it's "clean" as in git status says nothing to commit, working tree clean).

So, in short: tracked files are those in the proposed next commit, i.e., in Git's index aka staging area. These will be removed-and-replaced when switching commits, and if the target commit—the one you're switching tolacks the file, they will simply be removed.

That leaves us with the notion of untracked files, and the definition of these is simple: An untracked file is one that is in your working tree, but is not in Git's index / staging-area right now. It does not matter why it is not there right now. It might not be there because it never was there: not in any commit ever. It might not be there because it was not in the commit you switched to (e.g., perhaps commit b789abc has a file read.me but earlier commit a123456 doesn't, and you've switched to the earlier commit so the file isn't in the commit, but then you saved a copy into your working tree anyway). Or it might not be there because you ran git rm --cached. One way or another, the file is there in your working tree and isn't in the proposed next commit.

Given that the file isn't in the proposed next commit—the staging area—there's a significant chance that it's not in Git at all, in any commit. It could be in some commit; maybe it's one of the odder cases listed above. But usually some file like main.pyc (a Python byte-compiled file) isn't in any commit because you told Git never add this by listing it in a .gitignore, and you never added it, and hence never committed it. Python then went and created it, so it's sitting in your working tree. If it's a Python *.pyc file, it's pretty safe to go ahead and remove it.

But this isn't true of every untracked file. Sometimes some untracked file is a product of hours or even days of work, and you just have not yet run git add and git commit. It's precious, but it's the only copy ... and it is sitting around in your working tree, where Git may overwrite files.

Since it's not in any commit, this untracked file is at the moment in no danger. Switching from one commit to another will only remove-and-replace tracked files, and no commit in the repository has this file as a tracked file, so this file will remain untouched: your precious work is perfectly safe, even though it's in this rather risky location, in a Git working tree. Its untracked-ness and un-committed-ness is almost a sort of invisible shield, protecting the file. But there is one Git command that pierces this shield, and that is git clean. The git clean command's job is to remove untracked files.

You should therefore be very careful with git clean. It will remove untracked files, and these untracked files are, by definition, not in the proposed next commit and there's a very good chance they're not even in any commit. This means that Git cannot help you get these files back if you remove them. If they're computer-generated files, like our main.pyc example, that's no big deal, but if they will take hours or days of work to re-create, well, that is a Big Deal. So be careful with git clean!

  • Related