Reputation: 180
Here's my question. Say I have a repo with a file named 'app.js' on the master branch. I then make a new branch and checkout that branch. Is there a way to edit and save changes on the 'app.js' in the new branch, then switch back to the master and have the original file I can open back up in an editor? If I open the file in my text editor, it just saves it in the folder where I have it no matter which branch I am on. Sorry if this is a noob question, and thanks for the support!
Upvotes: 0
Views: 2030
Reputation: 45659
What you are describing is mostly default behavior of git, except there are a couple gotchas. Some background, and then some possible solutions:
Background
By "mostly default behavior", I mean: The point of branches is (in part) that you can switch back and forth between versions of a file, by switching between the branches.
But this only works for changes associated with the branch; and that association only occurs when you commit
the changes. When you just edit, save, and maybe stage the file, but without committing it - then your changes exist in the work tree and maybe in the index, but git doesn't associate them with the branch yet.
So when you run git checkout
to switch branches, probably git should issue a warning and refuse the checkout - and in some circumstances where data could otherwise be lost it will. But in the case where the file is the same in last commits to both branches, it will keep the changes in the worktree and/or index while switching branches. (At the command line, it does at least give you a little feedback to tell you this happened.
$ git checkout master
M file1
Switched to branch 'master'
says that uncommitted changes were carried over in file1
.)
Solutions
Commit
The first solution you might've seen in the comments is to commit your changes. If the changes are ready to commit, this is a good solution. Even if they aren't ready to be part of a permanent commit, as long as you don't push
the commit you could always edit it back out of history later - e.g. by doing the next commit with the --amend
option.
Stash
But maybe you have some staged changes, and additional unstaged changes, and you don't want to mix them together into a single commit just yet. Or maybe for some other reason you just don't feel that committing the changes is the right thing to do.
Another thing you can do is stash the changes. This really does still create temporary commits, but it tries to keep the staged and unstaged changes separate, and provides support for restoring your changes to their previous state. See git stash
documentation.
git checkout branch
# edit some stuff
git add .
# edit more stuff
git stash
# NOTE: at this point it will look like your changes have simply been
# reverted; but they haven't.
git checkout master
# now you have the `master` versions of all files
# ... do stuff ...
git checkout branch
git stash pop
# branch version of file is back
Multiple Work Trees
The stash
is not bad, but not always perfect.
Maybe you want uncommitted changes on multiple branches at the same time. This could be done with stashes, but then you have to keep things straight - like which stash belongs on which branch. (git
can help a little with this, but it's still easy to mess up.) So another solution to consider:
Your repo can have multiple work trees, each corresponding to a different branch.
git worktree add /path/for/additional/worktree branch
See git worktree
docs.
Now you have both versions of the file concurrently available at different paths on your filesystem, so any tool can "see" and operate on either or both.
This is arguably the most flexible interpretation of what you've asked for, but might be overkill if the other solutions are "good enough" for your use case.
Upvotes: 0
Reputation: 487725
Git, internally, can have an arbitrary number of different files all named app.js
, and can distinguish them by commit-hash or other commit specifier, such as HEAD
or a branch name:
git show HEAD:app.js
git show a9fc302:app.js
git show master:app.js
and so on. Whether your editor can do this, and if so how, is a question about your editor.
Note that your editor works with ordinary files stored in your computer's ordinary file storage system, in your work-tree, which is where Git lets you do your work. When working with Git, you must keep in mind that Git is mostly concerned with commits, which are (mostly) permament, (completely) read-only, snapshots of every file in the state that file had when you committed it. But the files that Git commits are not necessarily those from the work-tree!
Git stores Git's files in a special compressed format. Once files are committed, they are read-only and exist as long as the commit itself exists—but since they are in a special Git-only format, only Git can access them directly. What git checkout
does when you check out some commit (via some branch name, e.g., git checkout branch
) is to extract those read-only files:
First, Git copies them to Git's index. The index is also called the staging area, or sometimes the cache. In the index, the files still have a special Git-only form, but now they can be overwritten.
Then—well, really, at the same time as copying into the index, because it's more efficient—Git extracts the index version of each file into your work-tree, de-compressing the special Git-only form into a form that the rest of your computer system can work with.
So the work-tree copies of the files that you work on are not actually Git's files at all. When you run git add file
, you are telling Git to copy the work-tree version of file
into Git's index. At this time, Git compresses the file down to the special Git-only format and overwrites the current index version of that file with the new replacement.
The result is that the index always contains, ready to commit, whatever files will go into the next commit you make. It starts out with all the files from the current commit, in the special Git-only form, which match all of the files in your work-tree in their normal everyday form that your editor understands. But after that point, you can change, mangle, fold, spindle, or mutilate the work-tree files however you like. This has no effect on the version in the index, until and unless you run git add
to copy the work-tree file back into the index, overwriting the index version.
This is the difference between a change not staged for commit and a change staged for commit: running git status
has Git compare the current commit to the index, and whatever is different is staged for commit because you copied it into the index. Then, git status
has Git compare the index to the work-tree: whatever is different is not staged for commit because you have not yet copied it into the index.
Your editor cannot work directly on index files.1 It must work on work-tree files. For your own sanity, as well as ease of working with most standard editors, it makes more sense to save multiple different versions of some file under multiple different names, in or out of your work-tree area.
1There might be some that can, but if so, they probably do it by extracting the index file to a regular file, and then use git add
to put it back into the index. The internal format of the index is subject to change in each new Git version, so it's unwise to depend too much on it.
Upvotes: 4