SantoshGupta7
SantoshGupta7

Reputation: 6197

How to undelete a file whose deletion was pushed?

I deleted a file, and pushed those changes. I am wondering what's the best way to undelete this file.

I am thinking of adding the file back in, and pushing the change. Is this the best way to go about it?

Edit:

Since the push, there have been other code pushes, but I do not want to reverse those pushes.

I am using GitHub

Upvotes: 0

Views: 164

Answers (2)

Marco Bonelli
Marco Bonelli

Reputation: 69522

You have different options and different methods depending on if the file was the only one modified in that commit or not.

  1. Move the branch to the commit before the file was deleted, and force-push it to remote. This is ok if you are the only one working on the repository or in any case you are sure that nobody has already pulled the change from remote, because in that case it would create local conflicts for them. This removes any trace of the deletion from the history, since the commit that deleted the file will be lost when force-pushing.

    If the bad commit is the last commit, you can do this with:

    git reset HEAD~1
    git push --force
    

    If it's not the last commit I would not advise to use this method as it would rewrite every commit after it changing its hash (and this leads to problems if working with more collaborators), but it could still be done with an interactive rebase:

    git rebase --interactive <hash_of_bad_commit>~1
    # choose "delete" on the bad commit
    git push --force
    

    When asked by git rebase in the editor that pops up, change the line of the bad commit to d (or delete) and save. The rebase should be automatic and you can then force push. You can get the hash of the bad commit looking at the output of git log.

    If that was not the only file modified by the commit, then in the rebase you would have to select e (or edit) for the bad commit, resolve the issue yourself and then commit and continue the rebase:

    git rebase --interactive <hash_of_bad_commit>~1
    # choose "edit" on the bad commit
    git checkout HEAD~1 <path_to_deleted_file>
    git commit
    git rebase --continue
    git push --force
    
  2. Create a revert commit which undoes the action of the bad one where you deleted the file and then push to remote. This is the easiest and safest option. You can do this with:

    git revert <hash_of_bad_commit>
    git push
    

    Again, you can get the hash of the bad commit just by looking at git log.

    If that was not the only file touched by the bad commit, you can selectively revert the changes to that specific file using git reset like this:

    git reset <bad_commit_hash> <path_to_deleted_file>
    git commit
    git push
    
  3. Manually add the file back and create a new commit. This will have the same effect as the second option, but is done manually.

I would recommend option 1 if you want a clean history and are working alone, option 2 if you don't care about the commit history or if you are working on a repository with multiple contributors. There really is no reason to go with option 3, as the data is already available in the GIT history, you don't need to manually re-add the file and that could cause trouble if you add a wrong/different version of it.


Addressing your edit:

Since the push, there have been other code pushes, but I do not want to reverse those pushes.

I would definitely suggest you to go with option 2 above then.

Upvotes: 2

torek
torek

Reputation: 490048

Edit: Note that the OP added key information to the question; the first part of this answer assumes something that is not the case.


Every commit has a full and complete snapshot of every file (well, every file that is in that commit).

What this means is that you have a series of snapshots:

...--F--G--H   <-- somebranch, origin/somebranch

where H holds the latest snapshot, containing some set of files. Since you deleted a file, H has one fewer files than snapshot G.

If you make a new snapshot that has the file again, you get:

...--F--G--H   <-- origin/somebranch
            \
             I   <-- somebranch

where the snapshot in I matches that in H except that the file that is missing in H is present in I. When you git push this commit to the other Git, the other Git should accept it and add it to their somebranch, so that you now have:

...--F--G--H--I   <-- somebranch, origin/somebranch

Commit H continues to exist, and continues to lack the file. As long as that's fine, that's fine.

You cannot change commit H, at all, ever, so if that's not fine, you'll have to convince that other Git to discard its copy of commit H. In general, the way you do that is to discard yours as well, by making a new and improved commit H that replaces H:

          H   <-- origin/somebranch
         /
...--F--G--H'  <-- somebranch

where H' does have the file. Then you use a force-push variant of git push to tell the other Git that you know that this request will make them throw away their commit H and replace it with the new and improved H', and yes, you want them to do that.

If someone else has made their own commit that depends on existing commit H, this is not very nice to the someone-else. Make sure no one else depends on any commit(s) you will remove in this way. If you're the only one using the other Git repository, that means you just need to agree with yourself. If other people use the other Git repository too, get their agreement first.

(If it's OK to leave H in place, that's almost always easier and better.)


Re your edit: That piece of information is hugely important. This means there are already some other commits I, J, etc., that come after the H that is missing the file. Commits I, J, etc., presumably are also missing the file. If you build a new-and-improved replacement commit H', you must also build a new-and-improved replacement I', J', etc.

Upvotes: 1

Related Questions