How to Not Transfer Changes Done to Files from a Branch to a Another in Git

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 commitin 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 checkoutmaster

$ 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 to final

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 commit H, we can have Git look it up. That gets both the snapshot, and the actual hash ID of its parent commit G. We can have Git look that up, which gets an earlier snapshot, and the hash ID of even-earlier commit F, 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 to master. The existing commits didn't change: H still points back to G, which still points back to F, and so on. All we did was add a new commit I that points back to H. When we did, since we were using master, Git wrote I's ID into the name master.

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 repository final

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.

  1. 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 on develop.

  2. 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). Run git stash save or git stash push,1 or just plain git stash which is short for save / 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 apply

    If 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:

  1. Create a new branch off master: git branch <feature/new> master
  2. Checkout the branch: git checkout <feature/new>
  3. 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



Leave a reply



Submit