mdcq
mdcq

Reputation: 2026

Move file to another branch

I have a file on the main branch that should have been in a separate branch. It may be an unfinished file that should have been on a separate branch to be merged later. But now I can't rewrite the history of main anymore.

What is the simplest way to move the file to a different branch and delete it from main?


I tried the following

  1. Create a new feature branch. The file is still there in feature.
git switch -c feature
  1. Delete the file from main.
rm file
git commit -a -m "move file to feature branch"
  1. Rebase feature onto main.
git rebase main feature

I expected the file to look like it was added to feature. Instead, feature dropped the file and points to the very same commit as main. How can I prevent rebase from dropping the file?

Upvotes: 0

Views: 1589

Answers (1)

torek
torek

Reputation: 487815

You're starting from a bad assumption. This is leading you to do the wrong thing:

  • Files, in Git, are not on branches—not in any meaningful sense anyway.

  • Files in Git are stored in commits. The commits are then reachable from branches: often more than one.

Commits, once made, are frozen forever. You cannot fix an old commit. As long as you're the only one with some bad commit, though, you can just stop using it, by making new-and-improved commits that you use instead. That's what people call "rewriting history".

When nobody else has the bad commits, this rewriting works pretty well: nobody else knows you did it, so as long as you never look back and regret it, all is great! When someone else does have the bad commits (because you sent them off with git push for instance), it leads to sadness and pain, as they don't know to stop using the old bad commits and switch to the new-and-improved ones instead.1

In any case, given your setup, here's what to do:

  1. Make a new commit on main that removes the file. (You already did this, I'm just repeating it without the git commit -a.2)

    git checkout main && git rm file && git commit -m ...
    
  2. Create the feature branch from the updated main. There is now no need to rebase.

  3. Extract the file. It's not in the latest commit on main, but it's in the earlier commit on main. Since you have git switch, you have git restore, which is the newer-and-better way to get a file from an old commit:

    git restore --source=main~1 -SW file
    

    The -SW tells Git to put the file in both the staging area and your working tree. git status will now tell you that the file is new and is "staged for commit" (because it's in the staging area, in the form Git needs it in for future commits, and that copy in the staging area matches the working tree copy).

    You are now ready to run git commit to create a new commit on feature that contains this file (along with every other file).

  4. Run git commit to make a new commit. This will result in a single commit on feature that is not on main. The parent of this new commit will be the commit that is at the tip of main, in which the file is removed; the parent of that commit is the commit that was, earlier (before step 1) at the tip of main, that had the extra file.

Depending on who has which commits, and whether everyone else that might have them is prepared for history rewriting (see footnote 1), you may be able to use alternative methods for creating commits in which the original mistake never occurred. But this particular sequence says, in effect: oops, didn't want this file yet on main and then here we are on feature, let's bring the file back now.


1If they do know to do this, that leads to the new and improved rule on history rewriting. The old rule was "only do it if no one can tell"; the new improved rule, on replacing old commits with new improved commits, is "only do it if everyone agrees that it is to be done, and knows how to handle that".

2I consider git commit -a a bad habit. It's a nice way to sidestep learning about Git's index AKA staging area for a while. But Git has this way of reaching out and slapping you with its index anyway, like the Monty Python sketch, so it's wise to just give in and learn about it.

Upvotes: 1

Related Questions