checked out with unsaved changes

I've incidently switched to the branch develop from the branch analytics, where I had unsaved changes. Now I'm trying to switch back to the analytics branch and get the following error:

error: The following untracked working tree files would be overwritten by checkout:
application/db/some.sql
modules/analytics/config/database.php

How can I fix the issue without loosing data?

Answers


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:

  1. 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;
  2. remove the file entirely (while on branch master), so that git checkout dev will succeed, then git checkout dev;
  3. use git checkout -f dev to wipe out the file and get onto branch dev all at once (same as method 2);
  4. 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;
  5. 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".)


You can use git stash git stash -u. Git Stash


If the intent is to take the current changes and apply them to the branch analytics then you should do the following

git stash
git checkout analytics
git stash pop

The stash command will store the current branch changes and reset your working directory to HEAD. Once you've switched to the analytics branch you can reapply the changes by using stash pop. This will apply the most recent stash command to the current working directory


Need Your Help

Has anyone seen Control.SendToFront?

winforms .net-2.0

Hello I want to send a control to the front, but the only method I find in the Control class related to that is SendToBack().

Excel VBA range after filtering xlCellTypeVisible

excel vba excel-vba filter range

What I want to accomplish: Open a workbook from a specific address, Filter the first column for value equal to 36 or 541 (I got this first part working), then check column 3 to see if a value of 2 ...