How do I not transfer changes done to files from a branch to a another in git
When I do:
$ git init
Initialized empty Git repository in MyPath/Test1/.git/
then I create a file test.txt
$ touch test.txt
$ git add test.txt
$ git commit -m " commit 1 "
Now I want to modify it in a different branch
$ git checkout -b branch1
$ echo "Branch 1" >> test.txt
Here it's the tricky part...
If I don't add the file using git add test.txt
and I don't commit but directly go bak to master:
$ git checkout master
Switched to branch 'master'
M test.txt
I will see the modifications in master
!!:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: test.txt
What I should do is to add
and commit
in branch1:
$ git checkout branch1
$ git add test.txt
$ git commit -m "commit from branch 1"
[branch1 1b562f5] commit from branch 1
1 file changed, 1 insertion(+)
$ git log
commit 1b562f5be40a50df04f8a2a15b58a7af61742275 (HEAD -> branch1)
Author: xxxxxx<xxxxx.xxxx.xxx@xxxx.com>
Date: Thu Jun 3 16:36:30 2021 +0200
commit from branch 1
commit 03684e2a02c1a37a8f4546f296c6802686c7a4e9 (master)
Author: xxxx<xxx.xx.xx@xxxx.com>
Date: Thu Jun 3 16:31:05 2021 +0200
commit 1
And if I go back to master
and check the log:
$ git checkout master
Switched to branch 'master'
$ git log
commit 03684e2a02c1a37a8f4546f296c6802686c7a4e9 (HEAD -> master)
Author: xxxxx<xxxxx.xxxx.xxxxx@xxxx.com>
Date: Thu Jun 3 16:31:05 2021 +0200
commit 1
We won't see the modification ... as expected
SO! what if you want to work on other branches even if you haven't finished with the current branch?
I go back to branch1 and cat
its content
$ git checkout branch1
Switched to branch 'branch1'
$ cat test.txt
Branch 1
keep editing
$ echo "Branch 1..1" >> test.txt
Lets add
but not commit
and try to checkout
master
$ git add test.txt
$ git checkout master
error: Your local changes to the following files would be overwritten by checkout:
test.txt
Please commit your changes or stash them before you switch branches.
Aborting
not posible! you have to commit
before, BUT! you don't want a new commit every tima you change branche, so you can just commit --amend
$ git commit --amend
$ git log
commit 40a3a66e2b45a253f9a6ed564e977887c0748bf0 (HEAD -> branch1)
Author: xxxxx<xxxxx.xxxxx.xxxxx@xxxxx.com>
Date: Thu Jun 3 16:36:30 2021 +0200
commit from branch 1 => I can edit the comment!!
commit 03684e2a02c1a37a8f4546f296c6802686c7a4e9 (master)
Author: xxxxx<xxxxx.xxxxx.xxxxx@xxxxx.com>
Date: Thu Jun 3 16:31:05 2021 +0200
commit 1
And now i can safely git checkout master
Moving files and changes from one branch to another branch
Starting from the end and working backwards—which, incidentally, is what Git does too—let's look at this:
invalid upstream final
This suggests you ran git rebase final
, but do not yet have a branch name final
.
I am trying to move and push all files from
refactor
tofinal
There's several important things to know here, starting with this: Git isn't really about files. We'll come back to that in a moment, and finish going backwards now:
But currently, I have two branches under master ...
Branches—more precisely, branch names—do not have any kind of over/under relationships. (This going-backwards stuff is kind of a pain, isn't it? But it's what Git does, so it's good exercise to do it for a bit. )
Now, the thing to know about Git here is that it's all about commits. It's not about files or branches. Files and branches are important, of course, but as far as Git is concerned, it's the commits that matter. A commit holds files, and we find a commit using a branch name. But Git is all about commits. What you might like to do, then, is move the commits, but that might also be a problem. Here's what you need to know.
Git is all about commits
Since Git is about commits, you need to know exactly what a commit is and does for you. So you must memorize a few things:
Every commit is numbered. They're not counting numbers, like 1, 2, 3; but each commit has a unique number. The numbers look random, and are big and ugly and have letters in them too:
385c171a018f2747b329bcfa6be8eda1709e5abd
for instance. These numbers are actually cryptographic checksums of everything inside the commit. Git looks up the commit by this number.Each commit has two parts, the data and the metadata:
The data in a commit is simply a snapshot of every file that Git knew about when you (or whoever) made the commit. These files are stored in a special, read-only, Git-only, compressed and de-duplicated form. The de-duplication handles the fact that most commits mostly just re-use all the files from the previous commit.
The metadata in a commit contains stuff like your name and email address, and any log message you want to put in. Git adds its own stuff to this metadata, though: every commit stores the commit-number—the hash ID—of the previous commit. Git calls this the parent of the commit.
Most of everything else in Git falls out of these facts:
Git doesn't store changes, but rather snapshots.
The commits get strung together, in backwards-looking chains:
... <-F <-G <-H
Here
H
is the hash ID of the last commit in the chain. If we know the actual hash ID of commitH
, we can have Git look it up. That gets both the snapshot, and the actual hash ID of its parent commitG
. We can have Git look that up, which gets an earlier snapshot, and the hash ID of even-earlier commitF
, and so on.Because the number of a commit is a cryptographic checksum, it's actually not possible to change anything about a commit. If you take one out and make some changes to it and store the result, you get a new and different commit, with a different hash ID. The old commit is still there, unchanged.
A branch name just holds the hash ID of the last commit in the chain.
When you do make a new commit, Git writes the new commit's ID into the name:
...--F--G--H--I <-- master
Here, we've added a new commit
I
tomaster
. The existing commits didn't change:H
still points back toG
, which still points back toF
, and so on. All we did was add a new commitI
that points back toH
. When we did, since we were usingmaster
, Git wroteI
's ID into the namemaster
.
So the names move, but the commits don't change at all.
Now we're ready to look at "moving commits"
When you use Git, you generally start with git checkout branch
. This copies the files out of one commit—remember, the branch name specifies one commit, and the files inside the commit are in a read-only, Git-only form—to where you can use them. It also tells Git that that name is your current name, and therefore that particular commit is your current commit. That's what these drawings:
...--G--H <-- master (HEAD)
are all about. The special name HEAD
is attached to a branch name, like master
. That's your current branch, and the commit to which this points is your current commit.
If you now make a new branch name, e.g., refactor
, you get this:
...--G--H <-- master (HEAD), refactor
Once you switch to the name refactor
, you have this:
...--G--H <-- master, refactor (HEAD)
Either way, you're still using commit H
. It's just that the name for commit H
is either master
or refactor
.
Now you make new commits, in the usual way. If you're on refactor
when you do that, this is what happens:
...--G--H <-- master
\
I--J <-- refactor (HEAD)
Here, you've made two new commits. These snapshots, J
being the last one, contain files and metadata. J
points back to I
, which points back to H
. The name refactor
selects commit J
and the name master
selects commit H
.
I [would] like to commit all changes from repository
refactor
to repositoryfinal
Hang on: you just said repository, but earlier, you said branch.
A repository is a collection of commits (with branch names, which help you and Git find the commits). A branch is ... well, it's ambiguous: people use the word to mean different things. But a branch name is a name that helps you and Git find one specific commit, from which Git works backwards as needed.
If you want a new branch name spelled final
that identifies commit J
, that's easy:
git branch final # makes `final` point to the current commit
or:
git branch final refactor # makes `final` point to the same commit as `refactor`
The end result might look like this—note that again, no commits have actually changed:
...--G--H <-- master
\
I--J <-- refactor (HEAD), final
Git uses words in a very confusing manner
Git has a bunch of technical terms, such as remote, repository, branch, and so on. Not everyone actually uses these terms the right way (and some of them are just not very good in the first place, and some have evolved over time). If you're trying to get a question across to a human, or to get Git to do something, it can be very important to use the right terms, or to add a bit of explanation if you're not sure about terminology.
Git has a glossary to help out here. It's definitely not perfect, but it's a starting point, at least.
Why do changes on a new branch get carried over after switching to a different branch?
The reason you're seeing this is because you haven't committed the changes. You've edited the working copy and possibly run git add
, but haven't run git commit
.
Git will let you check out another branch with changes in the working copy provided it can preserve those changes. For example, if you've changed a file and that file is the same in both branches, Git doesn't need to change the file during checkout, and so it will preserve the version in your working tree. This makes it much easier if you realize you're accidentally on the wrong branch or need to make a new topic branch.
If you want to preserve your changes on a branch, you need to commit them; Git won't track them as part of that branch otherwise. Simply making changes to the working copy with a given branch checked out doesn't preserve them in any way.
If your working tree is clean (that is, it has no uncommitted changes), git checkout
will completely change the state of your working tree to another branch.
git switch branch without discarding local changes
There are a bunch of different ways depending on how far along you are and which branch(es) you want them on.
Let's take a classic mistake:
$ git checkout master
... pause for coffee, etc ...
... return, edit a bunch of stuff, then: oops, wanted to be on develop
So now you want these changes, which you have not yet committed to master
, to be on develop
.
If you don't have a
develop
yet, the method is trivial:$ git checkout -b develop
This creates a new
develop
branch starting from wherever you are
now. Now you can commit and the new stuff is all ondevelop
.You do have a
develop
. See if Git will let you switch without
doing anything:$ git checkout develop
This will either succeed, or complain. If it succeeds, great! Just
commit. If not (error: Your local changes to the following files would be overwritten ...
), you still have lots of options.The easiest is probably
git stash
(as all the other answer-ers
that beat me to clicking post said). Rungit stash save
orgit stash push
,1 or just plaingit stash
which is short forsave
/push
:$ git stash
This commits your code (yes, it really does make some commits) using
a weird non-branch-y method. The commits it makes are not "on" any
branch but are now safely stored in the repository, so you can now
switch branches, then "apply" the stash:$ git checkout develop
Switched to branch 'develop'
$ git stash applyIf all goes well, and you like the results, you should then
git stash drop
the stash. This deletes the reference to the weird non-branch-y commits. (They're still in the repository, and can sometimes be retrieved in an emergency, but for most purposes, you should consider them gone at that point.)
The apply
step does a merge of the stashed changes, using Git's powerful underlying merge machinery, the same kind of thing it uses when you do branch merges. This means you can get "merge conflicts" if the branch you were working on by mistake, is sufficiently different from the branch you meant to be working on. So it's a good idea to inspect the results carefully before you assume that the stash applied cleanly, even if Git itself did not detect any merge conflicts.
Many people use git stash pop
, which is short-hand for git stash apply && git stash drop
. That's fine as far as it goes, but it means that if the application results in a mess, and you decide you don't want to proceed down this path, you can't get the stash back easily. That's why I recommend separate apply
, inspect results, drop
only if/when satisfied. (This does of course introduce another point where you can take another coffee break and forget what you were doing, come back, and do the wrong thing, so it's not a perfect cure.)
1The save
in git stash save
is the old verb for creating a new stash. Git version 2.13 introduced the new verb to make things more consistent with pop
and to add more options to the creation command. Git version 2.16 formally deprecated the old verb (though it still works in Git 2.23, which is the latest release at the time I am editing this).
moving changed files to another branch for check-in
git stash
is your friend.
If you have not made the commit yet, just run git stash
. This will save away all of your changes.
Switch to the branch you want the changes on and run git stash pop
.
There are lots of uses for git stash. This is certainly one of the more useful reasons.
An example:
# work on some code
git stash
git checkout correct-branch
git stash pop
Update: No need to use stash
command. uncommitted changes do not belong to any branch so just use git checkout -b <new-branch>
transfer unstaged changes of one branch into another
Assume you know that staging is different from committing, you can try extracting the diff and apply them later:
First, extract the diff (make sure the diff file is outside your repository, or ignored in .gitignore
):
git diff > ../unstaged.diff
Then, drop the unstaged changes:
git stash save --keep-index --include-untracked
Checkout the target branch and apply the diff:
git checkout v4.0.1
git apply ../unstaged.diff
I want to take some files from a feature branch and move changes to another branch
Given exactly what you are asking for (to create a single commit containg all the changes from some files) you want to:
- Create a new branch off
master
:git branch <feature/new> master
- Checkout the branch:
git checkout <feature/new>
- Checkout the wanted files from the old feature branch to the new one:
git checkout <feature/old> -- <files to checkout>
.
I feel like this is one of the less well know uses of git checkout
. It pulls just the wanted files to your current branch without changing branch.
(Git documentation on this usage of checkout)
Bonus points: if, some some reason, a file has changes that you both need and don't need you can utilise the even more obscure --patch
option (git checkout --patch <feature/old> -- <files to checkout>
). This option invokes the interactive picker, which allows you to select specific hunks to include. The interactive mode can get some getting use to, but is helpful when you need to split things apart. It also exists on the add and reset command.
(Git's documentation on Interactive mode)
Move existing, uncommitted work to a new branch in Git
Update 2020 / Git 2.23
Git 2.23 adds the new switch
subcommand in an attempt to clear some of the confusion that comes from the overloaded usage of checkout
(switching branches, restoring files, detaching HEAD, etc.)
Starting with this version of Git, replace the checkout command with:
git switch -c <new-branch>
The behavior is identical and remains unchanged.
Before Update 2020 / Git 2.23
Use the following:
git checkout -b <new-branch>
This will leave your current branch as it is, create and checkout a new branch and keep all your changes. You can then stage changes in files to commit with:
git add <files>
and commit to your new branch with:
git commit -m "<Brief description of this commit>"
The changes in the working directory and changes staged in index do not belong to any branch yet. This changes the branch where those modifications would end in.
You don't reset your original branch, it stays as it is. The last commit on <old-branch>
will still be the same. Therefore you checkout -b
and then commit.
Move Specific Files from One Branch to Another Branch in Github
git stash
only works with the staging area to build up a commit. Once the changes are committed, it has no place. If you want to move committed changes to certain files, you need to make new commits with only those changes.
From now on I'll call branch-30 "source" and branch-31 "dest".
I'll assume that by "move" you want to remove the changes from source and add them to dest. There's many ways to do this. The important thing to account for is because dest may have also changed the files, if we tried to just move the changes in source to dest there might be conflicts. Git has a tool to handle this, merging!
First, we'll obtain the changes from source to dest by faking a merge. git merge -n
will perform a merge without committing them.
git checkout dest
git merge -n source
Now you have the uncommitted merge of everything. Use git restore
to remove any changes you do not want, and fix any conflicts as normal. Then stash that change and, since we don't actually want to merge, abort the merge: git merge --abort
.
Now apply and commit your stash to dest.
git stash pop
git commit
That's adding the changes to dest sorted, now we need to remove them from source. This is a similar approach, but instead of merging to get the changes, we'll use git restore
to bring all the files back to their original master versions, but not commit them.
git checkout source
git restore --source master .
Once again, you have a staging area full of changes. Again, remove all the changes you do not want. Then commit them.
And you're done.
Related Topics
Elf Header or Installation Issue with Bcrypt in Docker Container
Splitting Gzipped Logfiles Without Storing the Ungzipped Splits on Disk
Deleting String Up to the First Occurrence of Certain Character
-Bash: /Usr/Bin/Yum: /Usr/Bin/Python: Bad Interpreter: No Such File or Directory
How to Run Multiple Tor Processes at Once with Different Exit Ips
How to Capture Raw Hid Input on Linux
Postgresql Won't Start: "Server.Key" Has Group or World Access
How to Read Ring Buffer Within Linux Kernel Space
Delete a Column from a Delimited File in Linux
Xdp Program Ip Link Error: Prog Section Rejected: Operation Not Permitted
Identifying Which Linux System Library Contains a Function
Monitoring Pthread Context Switching
Why Does Order in Which Input Libraries Are Specified Matter
Svn Checkout the Contents of a Folder, Not the Folder Itself