The error message you see indicates that the files in question are untracked in your current branch, but tracked in the branch you want to switch to.
Here's an example, using just one file:
$ git checkout -b dev # create new branch dev (I was on master)
Switched to a new branch 'dev'
$ echo contents > dev-file
$ git status
On branch dev
Untracked files:
(use "git add <file>..." to include in what will be committed)
dev-file
nothing added to commit but untracked files present (use "git add" to track)
$ git add dev-file
$ git commit -m 'dev-file is tracked in dev'
[dev 24e6171] dev-file is tracked in dev
1 file changed, 1 insertion(+)
create mode 100644 dev-file
So now there's a new file, dev-file
, that is tracked in branch dev
.
$ git checkout master # get back to situation without dev-file
Switched to branch 'master'
$ ls
LICENSE.txt x.txt
We see that dev-file
does not exist here. Let's create it.
$ echo contents > dev-file
$ git checkout dev
error: The following untracked working tree files would be overwritten by checkout:
dev-file
Please move or remove them before you can switch branches.
Aborting
$
The question becomes, what do you want to do about the file(s)?
In this case, we created dev-file
with the same contents it would/will have, if/when we manage to git checkout dev
. We can see that with git diff
to compare:
$ git diff dev:dev-file dev-file
$
If we replace this untracked file with different contents (remember, we're still on master
at this point), and try the same git diff
, we'll see that they don't match:
$ echo different-contents > dev-file
$ git diff dev:dev-file dev-file
diff --git a/dev-file b/dev-file
index 12f00e9..462c93f 100644
--- a/dev-file
+++ b/dev-file
@@ -1 +1 @@
-contents
+different-contents
$
The question, though, is not really "do they match", but rather "what do you want to do about this situation" (in which dev-file is untracked in master
, tracked in dev
, and exists in the work directory while we are "on branch master
").
You can:
git add
and git commit
it (to make it tracked on the current branch, in this case master
): it's now saved permanently via the new commit;
- remove the file entirely (while on branch
master
), so that git checkout dev
will succeed, then git checkout dev
;
- use
git checkout -f dev
to wipe out the file and get onto branch dev
all at once (same as method 2);
- save the file somewhere other than a regular commit: e.g., use
git stash -u
as in aaronmallen's answer, or manually copy it somewhere outside the repository;
- invent some other solution. :-)
Methods 2 and 3 simply wipe out the version that is on your current branch, replacing it with the version that is on branch dev
. If git diff
says there's no difference between current-version and dev
-version, you obviously have not lost any actual file contents in the process (since it's preserved in branch dev
). If git diff
says there is a difference, you will have lost the non-dev
version.
In either case, though, if you switch back to master
, the file will vanish:
$ git checkout -f dev
Switched to branch 'dev'
$ cat dev-file
contents
(It's back to the version in dev
.)
$ git checkout master
Switched to branch 'master'
$ cat dev-file
cat: dev-file: No such file or directory
(It's gone. We can always retrieve the version in dev
, but the different-contents
we put in it earlier are gone.)
If you save the untracked files with git stash -u
, you'll have a place from which to recover the untracked files. Using git stash -u
like this will save a whole lot of stuff; see the documentation for git stash
.
If you save the untracked files outside of git entirely, you'll have a (non-git) place from which to recover the untracked files.
If you commit the untracked files into the original branch, you'll have a place—in fact, the branch itself, here master
—from which to recover the files, which are no longer untracked.
Which is the best solution? There's no one answer. Maybe they should be tracked. Maybe they should be "half tracked", as they are now. Maybe they should be untracked even in branch dev
(which means you'll have to do some operations on branch dev
to cause them to become untracked there: in particular, you'll have to check out that branch, use git rm --cached
or git rm
, and make a new commit in that branch). Git can't make this decision for you, nor can I; you need to figure out what you want to have happen with these files.
Edit: now that we know that the work directory got into this state via:
git --work-tree=/path/outside/here checkout develop
I can add a bit on this. I can't go into too much detail as I have not experimented with it much myself, nor gone through the index code in git with any kind of fine-toothed comb. But, one point of the index is to keep track of what's in your work tree.
It's very expensive, at least in a large project with a lot of code in a lot of files in a lot of directories, to search the entire work tree—but with some cleverness, git does not have to do that. Let's assume your work tree starts at .
, and you have modules/analytics/config/*
for instance. Inside the index (.git/index
), there are entries keeping track of stat
information about the files in that directory, as checked-out.
Git also knows that you're (as git status
would report) on branch analytics
. Then you invoke git checkout develop
.
Rather than searching the work directory for "what's there", git can use the data in the index to assume that "what's there" is what was last checked-out. In some (I'm not sure which) cases, git will re-do the stat
and know the index is out of date. In others (again I'm not sure which), I can see by observation that it does not do that: it just assumes that .git/index
describes the work tree.
If you always use the same work tree, with that (single) index, it "just works". If you use --work-tree
to point git to a different work tree, I've seen it do what amounts to: "ah, well, to switch from analytics
to develop
I only need to remove this file and replace it with that other one"—so that's all it does.
You can force git to use a different index file with GIT_INDEX_FILE=path
. For instance, if you wanted to track what goes into /path/outside/here
you could have a file named (e.g.) .git/index-alt
:
# note: this assumes $GIT_DIR is set, as it is with "git-sh-setup"
# so let's set it:
GIT_DIR=$(git rev-parse --git-dir) || exit
alt_index=${GIT_INDEX_FILE-"$GIT_DIR/index"}-alt
GIT_INDEX_FILE=$alt_index git --work-tree=/path/outside/here git checkout ...
This still has one other problem, that git checkout ...
will switch your current branch. To fix that, use an alternate form of git checkout
, telling it to check out all the files from the named branch, but without changing your current branch:
GIT_INDEX_FILE=$alt_index git --work-tree=/path/outside/here checkout branchname -- .
Now the "alternate index" tracks the alternate work tree, and HEAD
is never changed, so the local repository is never messed-with. (I have not tested this, but should work—it's something I've been planning to play with as part of "how best to deploy versions on servers using post-receive hooks".)