1038

I know that's rewriting of history which is bad yada yada.

But how to permanently remove few commits from remote branch?

Paolo Forgia
  • 6,572
  • 8
  • 46
  • 58
Arnis Lapsa
  • 45,880
  • 29
  • 115
  • 195
  • 106
    I know this is stupid, but sometimes shit happens - like testing logins and using plain text passwords in your code, which are real login credentials. And whoops... – frhd Dec 02 '13 at 13:35
  • 1
    Possible duplicate of [Delete commits from a branch in Git](https://stackoverflow.com/questions/1338728/delete-commits-from-a-branch-in-git) – wjandrea Sep 14 '17 at 19:15
  • 1
    Don't do it, it's will cause conflic to all other devs. – Rajat Feb 14 '19 at 09:46
  • 5
    @RajatVerma There will be no conflict if you are the only dev. Hell, even with 2-3 devs if they are all in the same room, you can easily resolve it. – Mark Lilback Nov 18 '19 at 18:36
  • 69
    I'm so tired of this academic spiel about how dangerous this is and how it should never be done yada yada. There are times where its far far far better to remove stuff from the git history and deal with the conflicts/breaking of other devs. It's really that simple. People who ignore this probably never worked outside of a classroom setting. – crush Feb 27 '20 at 16:24
  • @crush in my case - pushed upstream ~100 Mb large asset file that turned out to be unnecessary – Arnis Lapsa Mar 09 '20 at 08:06
  • 11
    @crush What happens most of the time is those "intelligent" people do not consider all the possible cases and lack perspective because they repeat something without thinking about a possible case that they do not consider at all. Sometimes rules must be broken. It reminds me of grammarNazis. – Alfonso Fernandez-Ocampo Jul 29 '20 at 17:21
  • 3
    I just pushed code with production database password in plain. Otherwise I have to change db password which will cause connection issues on multiple services because they are all using same db with same credentials. I know this is also bad practice but it works and I don't care. – TheMisir Sep 11 '20 at 19:52
  • If you accidentally commit real credentials, you need to assume someone has seen them before you could remove the commits, and you need to change those credentials anyway, at which point it hardly matters whether you remove the commits or not. (Some inflexible policies may require you to remove them anyway, of course.) – chepner Feb 04 '22 at 14:31

11 Answers11

700

You git reset --hard your local branch to remove changes from working tree and index, and you git push --force (or git push --force-with-lease) your revised local branch to the remote.
(other solution here, involving deleting the remote branch, and re-pushing it)

This SO answer illustrates the danger of such a command, especially if people depends on the remote history for their own local repos.
You need to be prepared to point out people to the RECOVERING FROM UPSTREAM REBASE section of the git rebase man page.

Plus, as noted by ringo in the comments, if the remote branch is protected against force push, a git revert, as in this answer, might be preferable.


With Git 2.23 (August 2019, nine years later), you would use the new command git switch.
That is: git switch -C mybranch origin/mybranch~n
(replace n by the number of commits to remove)

That will restore the index and working tree, like a git reset --hard would.
The documentation adds:

-C <new-branch>
--force-create <new-branch>

Similar to --create except that if <new-branch> already exists, it will be reset to <start-point>.
This is a convenient shortcut for:

$ git branch -f <new-branch>
$ git switch <new-branch>
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 1
    Strange. Feels like i tried that already. Together with some rebasing - works like a charm. Thanks. – Arnis Lapsa Jul 20 '10 at 19:29
  • 12
    @Arnis: perfect then ;) `push --force` away – VonC Jul 20 '10 at 19:39
  • I used [BFG Repo-Cleaner](https://rtyley.github.io/bfg-repo-cleaner/) to get rid of some sensitive data, that left me with an "unnamed" branch with the original file on it, after reseting to the last wanted commit and hard pushing to origin, all went fine (: – Rodrigo García Jul 16 '18 at 22:55
  • 1
    Notice that the commit's URL is still alive (at least for some time) so if someone has the commit's URL (you gave it to god knows why), they will be able to access the code. – Mosh Feu Jan 13 '20 at 12:40
  • 2
    @MoshFeu True: `git gc` is not always run often enough on the remote side. For instance on GitHub: https://twitter.com/githubhelp/status/387926738161774592?lang=es – VonC Jan 13 '20 at 13:58
  • 2
    @VonC Could you please clarify from your answer: If the new command is better or achieves the same end result? (You mentioned that the old command has some dangers!) – Porcupine May 12 '20 at 18:07
  • 1
    @Nikhil The new command is better: read https://stackoverflow.com/a/57066202/6309 and, for detached HEAD https://stackoverflow.com/a/3965714/6309. Here it does achieve the same result, just using more explicit arguments. – VonC May 12 '20 at 18:23
  • I'd appreciate more detail on the new command here now that it's been around for a while. – Cullub Jun 18 '20 at 19:31
  • @Cullub I have at least included the relevant part of the documentation for this new command. – VonC Jun 18 '20 at 20:04
  • I find ```git reset --hard``` not enough, I deleted the last pushed commit with ```git reset --hard HEAD~1``` and then pushed it. https://git-scm.com/docs/git-reset#Documentation/git-reset.txt-Undoacommitmakingitatopicbranch – lior.i Dec 23 '21 at 08:31
  • Wow, I always thought `git push --force` was a terrible idea, but today I pushed too many commits to a feature branch. Having rolled back locally, the force push was all that was needed to apply the same fix to the remote. Thanks! – jon_two Mar 24 '22 at 15:55
  • 1
    @jon_two [`git push --force-wirth-lease`](https://stackoverflow.com/a/52937476/6309) can be a bit safer. – VonC Mar 24 '22 at 16:03
  • Some protected branches do not allow force pushing. I would recommend `git revert` instead. – ringo Jun 01 '22 at 17:11
  • 1
    @ringo Good point. I have included your comment in the answer for more visibility. – VonC Jun 01 '22 at 18:38
  • And note the git push --force – Daniel Viglione Sep 21 '22 at 18:02
  • 1
    @DanielViglione True, that kind of operation (where you modify the history of a branch) will involve a force push. – VonC Sep 21 '22 at 19:54
675

Just note to use the last_working_commit_id, when reverting a non-working commit

git reset --hard <last_working_commit_id>

So we must not reset to the commit_id that we don't want.

Then sure, we must push to remote branch:

git push --force
hd84335
  • 8,815
  • 5
  • 34
  • 45
  • 29
    Perfect, elegant, simpliest answer. I just reverted to the last stabe commit I needed both on remote and locally – lauWM Dec 13 '17 at 15:13
  • 15
    This caused me to lose my local changes too. I wasn't expecting that. But meh, better than committing your personal password to work repo. – Airwavezx May 23 '18 at 08:47
  • 9
    you can save your local changes with git stash .... make some things .... and git stash pop (your local changes are back) – MonTea Jul 29 '18 at 13:55
  • This gives me error as "remote: error: denying non-fast-forward refs/heads/master (you should pull first)" – Saurabhcdt Nov 14 '18 at 09:30
  • 1
    @Airwavezx that is what `git reset --hard` is supposed to do. – Lucat Jan 31 '20 at 15:36
  • You should clarify that this will make you lose all your changes from that commit in remote you're trying to delete. I just lost all my work, thanks a lot dude. – Gonzalo Feb 10 '23 at 19:23
180

Important: Make sure you specify which branches on "git push -f" or you might inadvertently modify other branches![*]

There are three options shown in this tutorial. In case the link breaks I'll leave the main steps here.

  1. Revert the full commit
  2. Delete the last commit
  3. Delete commit from a list

1 Revert the full commit

git revert dd61ab23

2 Delete the last commit

git push <<remote>> +dd61ab23^:<<BRANCH_NAME_HERE>>

or, if the branch is available locally

git reset HEAD^ --hard
git push <<remote>> -f

where +dd61... is your commit hash and git interprets x^ as the parent of x, and + as a forced non-fastforwared push.

3 Delete the commit from a list

git rebase -i dd61ab23^

This will open and editor showing a list of all commits. Delete the one you want to get rid off. Finish the rebase and push force to repo.

git rebase --continue
git push <remote_repo> <remote_branch> -f
Elias Dorneles
  • 22,556
  • 11
  • 85
  • 107
jorge_codes
  • 3,026
  • 4
  • 26
  • 25
167

If you want to delete for example the last 3 commits, run the following command to remove the changes from the file system (working tree) and commit history (index) on your local branch:

git reset --hard HEAD~3

Then run the following command (on your local machine) to force the remote branch to rewrite its history:

git push --force

Congratulations! All DONE!

Some notes:

You can retrieve the desired commit id by running

git log

Then you can replace HEAD~N with <desired-commit-id> like this:

git reset --hard <desired-commit-id>

If you want to keep changes on file system and just modify index (commit history), use --soft flag like git reset --soft HEAD~3. Then you have chance to check your latest changes and keep or drop all or parts of them. In the latter case runnig git status shows the files changed since <desired-commit-id>. If you use --hard option, git status will tell you that your local branch is exactly the same as the remote one. If you don't use --hard nor --soft, the default mode is used that is --mixed. In this mode, git help reset says:

Resets the index but not the working tree (i.e., the changed files are preserved but not marked for commit) and reports what has not been updated.

Abdollah
  • 4,579
  • 3
  • 29
  • 49
24

TL:DR;

  1. git switch -C branch_name origin/branch_name~n
  2. git push --force

Done, remote branch will be reverted by n commits.


Explaination:

  1. Use git switch, resets the branch by n number of commits. The -C option will force create a new branch with same name.

    • replace branch_name with your branch name,
    • replace n (at the end of command), with number of commits you want to revert.

    Command #1: git switch -C branch_name origin/branch_name~n

    Example: git switch -C feature/dashboard origin/feature/dashboard~1 // This command reverts 1 commit in dashboard branch.

  2. Force push the local change

    Command #2: git push --force


Tip: To undo committed(unpushed) changes git reset HEAD~

Saif
  • 2,678
  • 2
  • 22
  • 38
15

Simplifying from pctroll's answer, similarly based on this blog post.

# look up the commit id in git log or on github, e.g. 42480f3, then do
git checkout master
git checkout your_branch
git revert 42480f3
# a text editor will open, close it with ctrl+x (editor dependent)
git push origin your_branch
# or replace origin with your remote
ted.strauss
  • 4,119
  • 4
  • 34
  • 57
  • 3
    Works for me, thanks. And I want to permanently delete one commit(eg. contains pwd) from remote branch history, how do this? – Smiles Feb 01 '19 at 03:53
  • 1
    Works for me too, but I have the same question than Smiles, I dont want to anybody see my commit/rever in the history.. how I remove that? – Roberto Rodriguez Mar 06 '19 at 15:09
12

This might be too little too late but what helped me is the cool sounding 'nuclear' option. Basically using the command filter-branch you can remove files or change something over a large number of files throughout your entire git history.

It is best explained here.

cmaher
  • 5,100
  • 1
  • 22
  • 34
jansmolders86
  • 5,449
  • 8
  • 37
  • 51
  • 10
    Ain't too late. Might become useful for wanderers with similar problems :) – Arnis Lapsa Mar 13 '14 at 11:44
  • This helped me, however the official Git doc now discourages using `filter-branch` in favour of https://github.com/newren/git-filter-repo – TanguyP Feb 21 '22 at 10:33
5

Sometimes the easiest way to fix this issue is to make a new branch from the place where you know the code is good. Then you can leave the errant branch history alone in case you need to cherry-pick other commits from it later. This also ensures you did not lose any commit history.

From your local errant branch:

git log

copy the commit hash that you wanted the branch to be at and exit the git log

git checkout theHashYouJustCopied
git checkout -b your_new_awesome_branch

Now you have a new branch just the way you want it.

If you also needed to keep a specific commit from the errant branch that is not on your new branch, you can just cherry-pick that specific commit you need:

git checkout the_errant_branch
git log

Copy the commit hash of the one commit you need to pull into the good branch and exit the git log.

git checkout your_new_awesome_branch
git cherry-pick theHashYouJustCopied

Pat yourself on the back.

Douglas.Sesar
  • 4,214
  • 3
  • 29
  • 36
  • very easy and who cares if you have a dead-legged branch? make a new branch and be done with it! – Andrew Fox Feb 27 '19 at 04:31
  • 1
    This is a truly excellent answer. I am glad I did this instead of cherry-picking commits, or manipulating the remote branch. – Magnilex Jun 05 '20 at 11:35
4

Here's a clean way of removing your commits from the remote repository without losing your work.

Quick Answer

git reset --soft HEAD~1 # 1 represents only last 1 commit 
git stash # hold your work temporary storage temporarily.
git pull # bring your local in sync with remote
git reset --hard HEAD~1 # hard reset 1 commit behind (deletes local commit)
git push -f # force push to sync local with remote
git stash pop # get back your unpushed work from stash

A detailed explanation of why this works.

Heres is what happened in my case

  1. I was working on a branch redesign

  2. I accidentally committed and pushed a secrets file along with my other work. So my secrets are now exposed (Gitguardian's yelling about this in my email xD) Now I want to remove my secrets file from the repository but want to keep my work too.

  3. So run git reset --soft HEAD~1 this will move your local repository on your machine 1 commit behind (modify the number to move n commits behind)

  4. Now you will see your committed files as unstaged

  5. Save this work locally by moving it temporarily to a stash by running git stash.

  6. Now make your local sync with the remote by running git pull

  7. Now run git reset --hard HEAD~1 (again modify the number to remove n commits) to remove the remote commits from your repository and do a force push git push -f.

  8. You will see your commits from your remote repository are removed.

  9. Now get back your work from stash by running git stash pop

  10. Now do the changes you need to with your unpushed work.

Mahesh Jamdade
  • 17,235
  • 8
  • 110
  • 131
1
 git reset --soft commit_id
 git stash save "message"
 git reset --hard commit_id
 git stash apply stash stash@{0}
 git push --force
ayushgp
  • 4,891
  • 8
  • 40
  • 75
pandorago
  • 66
  • 5
0

I like to do this using rebase. Below, n is the last n commits. So, if you want to delete the third one, replace n by 3.

git rebase -i HEAD~n

then, find the desired commit in the listing and change it from "pick" to "drop". Exit the rebase and use git push with the "-f" option as you just did a rebase.

John
  • 1
  • 1