506

I would like to put a Git project on GitHub but it contains certain files with sensitive data (usernames and passwords, like /config/deploy.rb for capistrano).

I know I can add these filenames to .gitignore, but this would not remove their history within Git.

I also don't want to start over again by deleting the /.git directory.

Is there a way to remove all traces of a particular file in your Git history?

Gonçalo Peres
  • 11,752
  • 3
  • 54
  • 83
Stefan
  • 9,289
  • 7
  • 38
  • 46
  • Related: [How to remove/delete a large file from commit history in Git repository?](http://stackoverflow.com/questions/2100907/how-do-i-purge-a-huge-file-from-commits-in-git-history) and [Completely remove file from all Git repository commit history](http://stackoverflow.com/questions/307828/completely-remove-unwanted-file-from-git-repository-history). –  Apr 04 '14 at 00:47
  • 1
    related https://help.github.com/articles/removing-sensitive-data-from-a-repository/ – Trevor Boyd Smith Jun 21 '18 at 18:59

12 Answers12

598

For all practical purposes, the first thing you should be worried about is CHANGING YOUR PASSWORDS! It's not clear from your question whether your git repository is entirely local or whether you have a remote repository elsewhere yet; if it is remote and not secured from others you have a problem. If anyone has cloned that repository before you fix this, they'll have a copy of your passwords on their local machine, and there's no way you can force them to update to your "fixed" version with it gone from history. The only safe thing you can do is change your password to something else everywhere you've used it.


With that out of the way, here's how to fix it. GitHub answered exactly that question as an FAQ:

Note for Windows users: use double quotes (") instead of singles in this command

git filter-branch --index-filter \
'git update-index --remove PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' <introduction-revision-sha1>..HEAD
git push --force --verbose --dry-run
git push --force

Update 2019:

This is the current code from the FAQ:

  git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" \
  --prune-empty --tag-name-filter cat -- --all
  git push --force --verbose --dry-run
  git push --force

Keep in mind that once you've pushed this code to a remote repository like GitHub and others have cloned that remote repository, you're now in a situation where you're rewriting history. When others try pull down your latest changes after this, they'll get a message indicating that the changes can't be applied because it's not a fast-forward.

To fix this, they'll have to either delete their existing repository and re-clone it, or follow the instructions under "RECOVERING FROM UPSTREAM REBASE" in the git-rebase manpage.

Tip: Execute git rebase --interactive


In the future, if you accidentally commit some changes with sensitive information but you notice before pushing to a remote repository, there are some easier fixes. If you last commit is the one to add the sensitive information, you can simply remove the sensitive information, then run:

git commit -a --amend

That will amend the previous commit with any new changes you've made, including entire file removals done with a git rm. If the changes are further back in history but still not pushed to a remote repository, you can do an interactive rebase:

git rebase -i origin/master

That opens an editor with the commits you've made since your last common ancestor with the remote repository. Change "pick" to "edit" on any lines representing a commit with sensitive information, and save and quit. Git will walk through the changes, and leave you at a spot where you can:

$EDITOR file-to-fix
git commit -a --amend
git rebase --continue

For each change with sensitive information. Eventually, you'll end up back on your branch, and you can safely push the new changes.

Black
  • 18,150
  • 39
  • 158
  • 271
natacado
  • 6,697
  • 1
  • 19
  • 8
  • [git filter-branch --index-filter 'git update-index --remove filename' ..HEAD] running this didn't rewrite the commit history, on running 'git log' still commit history is present. is there any spl thing to check ? – Arun Sep 19 '12 at 10:09
  • Just for the record: The github link does work, but I had to write the path of the file. I.e: src/path/to/file.extension – MigDus Nov 26 '12 at 22:55
  • 4
    Got this to work. I was lost in translations. I used the link instead of the command here. Also, Windows command ended up requiring double-quotes as ripper234 mentions, full path as MigDus suggests, and not including the "\" characters that the link pasted as new line wrapping indicators. Final command looked something like: git filter-branch --force --index-filter "git rm --cached --ignore-unmatch src\[Project]\[File].[ext]" --prune-empty --tag-name-filter cat -- --all – Eric Swanson Aug 29 '14 at 19:55
  • 3
    There seem to be some substantive differences between your `filter-branch` code and that in the github page you linked to. E.g their 3rd line `--prune-empty --tag-name-filter cat -- --all`. Has the solution changed or am I missing something? – geotheory Aug 22 '15 at 09:22
  • This didn't work for me, but the github provided solution (which is different) did. – tirdadc Jan 25 '16 at 14:36
  • For anyone using this.. for me 'introduction sha' means the commit BEFORE the file existed. if I provided the SHA of the commit the file was created in it didn't work. – Gizmo Feb 17 '16 at 02:33
  • Note that afterwards, you should `rm .git/refs/original/heads/[BRANCHNAME]` and `git gc --prune=all --aggressive` to get rid of leftovers from the filtering. But be aware of the consequences of using `--prune=all`, namely "Unless the repository is quiescent, you will lose newly created objects that haven’t been anchored with the refs and end up corrupting your repository" – Tobias Kienzler Jul 07 '16 at 11:48
  • 2
    This solution looks quite good, but if I've introduced the file to remove in the initial commit `..HEAD` doesn't work. It only removes the file from the second commit onward. (How do I include the initial commit into the range of commits?) The save way is pointed out here: https://help.github.com/articles/removing-sensitive-data-from-a-repository/ `git filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' \ --prune-empty --tag-name-filter cat -- --all` – white_gecko Jun 27 '17 at 10:23
  • I got an `ambiguous argument` error in Github Desktop on Windows, turns out there were invisible characters when pasting the sha1 copied from Github Desktop – raphael Jul 11 '17 at 14:00
  • This doesn't work in windows even with the double quotes. What's the ..HEAD about, doesn't seem valid – Stuart Dobson Jul 30 '17 at 01:52
  • Thank you for this. I finally able to get rid of my poor practice many years ago for a personal repository. – Ru Chern Chong Sep 09 '17 at 18:11
  • 2
    this command does not remove files in all branches, nor tags. [Official github help](https://help.github.com/articles/removing-sensitive-data-from-a-repository) works perfectly – Sang Dec 12 '17 at 04:57
  • This answer, as well as [this one](https://stackoverflow.com/a/29741586/8075923) are both of great value for the community. Thanks guys, +1. – Soutzikevich Jan 21 '19 at 13:57
  • You don't have to use double quotes on Windows if you use Git Bash instead of Git Cmd – mbomb007 May 29 '19 at 16:21
  • 1
    I get `fatal: refusing to merge unrelated histories` – Black Sep 11 '19 at 14:02
  • For me it work without backslashes on macOS. Reference this answer: https://stackoverflow.com/a/52137662/6753606 – SametSahin Apr 01 '20 at 00:31
  • DESTROYED my local repo! – marcolopes May 10 '21 at 21:48
  • Hi, could you please add the fact that one should also check with the support of the relevant host? Cleaning the git repo is not enough, you need to reach out to the repository host for further cleaning on their own servers. You also need to make the repositiory private as soon as possible if that's an option, it should be the first thing to do. This is not stressed enough in this answser imo. – Eric Burel Jan 14 '22 at 10:31
  • 1
    THIS DELETES THE FILE OFF THE OS FILE SYSTEM!!! `git filter-branch --force --index-filter "git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" --prune-empty --tag-name-filter cat -- --all` and the path is the RELATIVE path w/respect to the git directory, with no leading slash – toddmo Nov 07 '22 at 22:47
  • Did not work for me. File still existed in one of the earlier commits. – Myridium May 15 '23 at 21:52
138

Changing your passwords is a good idea, but for the process of removing password's from your repo's history, I recommend the BFG Repo-Cleaner, a faster, simpler alternative to git-filter-branch explicitly designed for removing private data from Git repos.

Create a private.txt file listing the passwords, etc, that you want to remove (one entry per line) and then run this command:

$ java -jar bfg.jar  --replace-text private.txt  my-repo.git

All files under a threshold size (1MB by default) in your repo's history will be scanned, and any matching string (that isn't in your latest commit) will be replaced with the string "***REMOVED***". You can then use git gc to clean away the dead data:

$ git gc --prune=now --aggressive

The BFG is typically 10-50x faster than running git-filter-branch and the options are simplified and tailored around these two common use-cases:

  • Removing Crazy Big Files
  • Removing Passwords, Credentials & other Private data

Full disclosure: I'm the author of the BFG Repo-Cleaner.

Community
  • 1
  • 1
Roberto Tyley
  • 24,513
  • 11
  • 72
  • 101
  • This is an option, but it could break your application when the passwords are used, e.g. to set up a database connection. I'd prefer the currently accepted answer because it's possible to still keep the passwords in your working copy and ignore the files containing them with .gitignore. – Henridv Feb 18 '13 at 12:45
  • 1
    @Henridv I'm not sure how the accepted answer by natacado differs in that respect from my own answer? Both of our answers specifically address the key sentence of the question: "Is there a way to remove all traces of a particular file in your Git history?" - ie they talk about Git history-rewriting. The issue of /how/ NixNinja /should/ supply passwords to his app isn't mentioned either in his question, or in any of the current answers. As it happens, the BFG specifically addresses the issue of unintended consequences, see http://rtyley.github.com/bfg-repo-cleaner/#protected-commits – Roberto Tyley Feb 18 '13 at 14:09
  • 10
    This is a big win right here. After a couple tries, I was able to use this to strip commits containing sensitive information from a private repo very thoroughly and forcefully update the remote repo with the revised history. One side note is that you do have to ensure the tip of your repo (HEAD) is itself clean with no sensitive data as this commit is considered "protected" and won't be revised by this tool. If it isn't, just clean/replace manually and `git commit`. Otherwise, +1 for new tool in developer's toolbox :) – Matt Borja Feb 27 '14 at 17:33
  • 2
    @Henridv Per my recent comment, it should not break your application as you might anticipate, assuming your application is currently situated at the tip or head of your branch (i.e. latest commit). This tool will explicitly report for your last commit `These are your protected commits, and so their contents will NOT be altered` while traversing and revising the rest of your commit history. If you needed to rollback, however, then yes you would need to just do a search for `***REMOVED***` in the commit you just rolled back to. – Matt Borja Feb 27 '14 at 17:37
  • 2
    +1 for BFG (if you have Java installed or don't mind installing it). One catch is that BFG refuses to delete a file if it is contained in HEAD. So it's better to first do a commit where the desired files will be deleted and only then run BFG. After that you can revert that last commit, now it doesn't change a thing. – Fr0sT May 28 '14 at 14:56
  • I have a project where I first wanted the project to work, and THEN went to add stuff like "be able to set the password". So at first there was a structure with "username" and "password" statically intialized in my source. Later I added the stuff to be able to set those. So: Now my source is "clean", but in git-history I have my credentials. As the project is now still private I'll have to run this before I publish. This looks promising! – rew Jan 09 '15 at 08:21
  • 1
    `git push --force` is missing – Gino Pane Jul 18 '19 at 09:13
  • it seems `git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *.ext' --prune-empty --tag-name-filter cat -- --all` does indeed delete the desired files from HEAD, despite the `--cached` flag Is this a differentiator for BFG in that it protects HEAD? – goofology Aug 08 '19 at 05:44
  • @goofology, yep, see https://rtyley.github.io/bfg-repo-cleaner/#protected-commits – Roberto Tyley Aug 08 '19 at 08:34
  • Thanks for the link. I'm still curious, am I misunderstanding the purpose of `git filter-branch --force --index-filter 'git rm --cached *.ext'`? It seems to function the same as `git filter-branch --force --index-filter 'git rm *.ext'` (assuming "clean" working branch") – goofology Aug 08 '19 at 15:33
  • 1
    If I didn't already love you for this answer, I do now after using the .jar you created: `"You can rewrite history in Git - don't let Trump do it for real! Trump's administration has lied consistently, to make people give up on ever being told the truth. Don't give up: https://www.aclu.org/"` – monstermac77 Jun 18 '20 at 06:18
  • [Replace text to text of your choosing](https://stackoverflow.com/a/15730571/13112739) – user760900 Sep 05 '20 at 20:43
47

git filter-repo is now officially recommended over git filter-branch

This is mentioned in the manpage of git filter-branch in Git 2.5 itself.

With git filter repo, you could either remove certain files with: Remove folder and its contents from git/GitHub's history

pip install git-filter-repo
git filter-repo --path path/to/remove1 --path path/to/remove2 --invert-paths

This automatically removes empty commits.

Or you can replace certain strings with: How to replace a string in whole Git history?

git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx')

If you pushed to GitHub, force pushing is not enough, delete the repository or contact support

Even if you force push one second afterwards, it is not enough as explained below.

The only valid courses of action are:

  • is what leaked a changeable credential like a password?

    • yes: modify your passwords immediately, and consider using more OAuth and API keys!

    • no (naked pics):

      • do you care if all issues in the repository get nuked?

        • no: delete the repository

        • yes:

          • contact support
          • if the leak is very critical to you, to the point that you are willing to get some repository downtime to make it less likely to leak, make it private while you wait for GitHub support to reply to you

Force pushing a second later is not enough because:

If you delete the repository instead of just force pushing however, commits do disappear even from the API immediately and give 404, e.g. https://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653f7a3824 This works even if you recreate another repository with the same name.

To test this out, I have created a repo: https://github.com/cirosantilli/test-dangling and did:

git init
git remote add origin git@github.com:cirosantilli/test-dangling.git

touch a
git add .
git commit -m 0
git push

touch b
git add .
git commit -m 1
git push

touch c
git rm b
git add .
git commit --amend --no-edit
git push -f

See also: How to remove a dangling commit from GitHub?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
  • 1
    If the repository is part of a fork network, **making the repository private or deleting it may not help and may make the problem worse**. Fork networks on GitHub seem to share an internal bare repository, so that commits in one fork are also retrievable through other forks. Making a repository private or deleting it causes a split from the fork network, with the sensitive commits now duplicated in each remaining bare repository. The commits will continue to be accessible through forks until GC has been run on _both_ bare repositories. – knuton Mar 23 '21 at 16:08
  • I tried `git filter-repo --replace-text <(echo 'my_password==>xxxxxxxx')` and while it did work, it also made all commits look like they happened today. Is there a way to preserve commit date while changing a file in history? – devordem Jan 20 '23 at 11:16
  • @devordem I don't reproduce, author committer dates were maintained for me on git 2.37.2, filter-repo ac039ecc095d, Ubuntu 22.10. – Ciro Santilli OurBigBook.com Jan 24 '23 at 08:00
  • 1
    Never mind, I later found out it was something else I did that rewrote the commit dates not `git-filter-repo`. – devordem Jan 24 '23 at 08:53
20

You can use git forget-blob.

The usage is pretty simple git forget-blob file-to-forget. You can get more info here

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

It will disappear from all the commits in your history, reflog, tags and so on

I run into the same problem every now and then, and everytime I have to come back to this post and others, that's why I automated the process.

Credits to contributors from Stack Overflow that allowed me to put this together

nachoparker
  • 1,678
  • 18
  • 14
19

I recommend this script by David Underhill, worked like a charm for me.

It adds these commands in addition natacado's filter-branch to clean up the mess it leaves behind:

rm -rf .git/refs/original/
git reflog expire --all
git gc --aggressive --prune

Full script (all credit to David Underhill)

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter \
"git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch
# otherwise leaves behind for a long time
rm -rf .git/refs/original/ && \
git reflog expire --all && \
git gc --aggressive --prune

The last two commands may work better if changed to the following:

git reflog expire --expire=now --all && \
git gc --aggressive --prune=now
Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113
  • 2
    Note that your usage of expire and prune are incorrect, if you don't specify the date then it defaults to all commits older than 2 weeks for prune. What you want is all commits so do: `git gc --aggressive --prune=now` – Adam Parkin Aug 18 '12 at 19:10
  • @Adam Parkin I'm going to leave the code in the answer the same because it is from the script on David Underhill's site, you could comment there and if he changes it I would change this answer since I really don't know git that well. The expire command prior to the prune doesn't affect that does it? – Jason Goemaat Aug 19 '12 at 21:32
  • 1
    @MarkusUnterwaditzer: That one won't work for pushed commits. – Max Beikirch Apr 12 '13 at 20:30
  • 1
    Maybe you should just put all the commands in your answer; it would be much more consistent and wouldn't require the mental combining of separate posts :) – Andrew Mao Aug 06 '13 at 20:34
16

Here is my solution in windows

git filter-branch --tree-filter "rm -f 'filedir/filename'" HEAD

git push --force

make sure that the path is correct otherwise it won't work

I hope it helps

vertigo71
  • 258
  • 3
  • 6
12

Use filter-branch:

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *file_path_relative_to_git_repo*' --prune-empty --tag-name-filter cat -- --all

git push origin *branch_name* -f
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
8

To be clear: The accepted answer is correct. Try it first. However, it may be unnecessarily complex for some use cases, particularly if you encounter obnoxious errors such as 'fatal: bad revision --prune-empty', or really don't care about the history of your repo.

An alternative would be:

  1. cd to project's base branch
  2. Remove the sensitive code / file
  3. rm -rf .git/ # Remove all git info from your code
  4. Go to github and delete your repository
  5. Follow this guide to push your code to a new repository as you normally would - https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/

This will of course remove all commit history branches, and issues from both your github repo, and your local git repo. If this is unacceptable you will have to use an alternate approach.

Call this the nuclear option.

lostphilosopher
  • 4,361
  • 4
  • 28
  • 39
4

In my android project I had admob_keys.xml as separated xml file in app/src/main/res/values/ folder. To remove this sensitive file I used below script and worked perfectly.

git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch  app/src/main/res/values/admob_keys.xml' \
--prune-empty --tag-name-filter cat -- --all
Ercan
  • 2,601
  • 22
  • 23
3

I've had to do this a few times to-date. Note that this only works on 1 file at a time.

  1. Get a list of all commits that modified a file. The one at the bottom will the the first commit:

    git log --pretty=oneline --branches -- pathToFile

  2. To remove the file from history use the first commit sha1 and the path to file from the previous command, and fill them into this command:

    git filter-branch --index-filter 'git rm --cached --ignore-unmatch <path-to-file>' -- <sha1-where-the-file-was-first-added>..

b01
  • 4,076
  • 2
  • 30
  • 30
2

So, It looks something like this:

git rm --cached /config/deploy.rb
echo /config/deploy.rb >> .gitignore

Remove cache for tracked file from git and add that file to .gitignore list

przbadu
  • 5,769
  • 5
  • 42
  • 67
1

Considering that OP is using GitHub, if one commits sensitive data into a Git repo, one can remove it entirely from the history by using one of the previous options (read more about them below):

  1. The git filter-repo tool (view source on GitHub).

  2. The BFG Repo-Cleaner tool (it is open source - view source on GitHub).

After one of the previous options, there are additional steps to follow. Check the section Additional below.

If the goal is to remove a file that was added in the most recent unpushed commit, read the section Alternative below.

For future considerations, to prevent similar situations, check the For the Future section below.


Option 1

Using git filter-repo. Before moving forward, note that

If you run git filter-repo after stashing changes, you won't be able to retrieve your changes with other stash commands. Before running git filter-repo, we recommend unstashing any changes you've made. To unstash the last set of changes you've stashed, run git stash show -p | git apply -R. For more information, see Git Tools - Stashing and Cleaning.

Let us now remove one file from the history of one's repo and add it to .gitignore (to prevent re-committing it again).

Before moving forward, make sure that one has git filter-repo installed (read here how to install it), and that one has a local copy of one's repo (if that is not the case, see here how to clone a repository).

  1. Open GitBash and access the repository.

    cd YOUR-REPOSITORY
    
  2. (Optional) Backup the .git/config file.

  3. Run

    git filter-repo --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
    

    replace PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA with the path to the file you want to remove, not just its filename to:

    • Force Git to process, but not check out the entire history of every branch and tag.

    • Remove the specified file (as well as empty commits generated as a result)

    • Remove some configs (such as remote URL stored in the .git/config file)

    • Overwrite one's existing tags.

  4. Add the file with sensitive data to .gitignore

    echo "YOUR-FILE-WITH-SENSITIVE-DATA" >> .gitignore
    
    git add .gitignore
    
    git commit -m "Add YOUR-FILE-WITH-SENSITIVE-DATA to .gitignore"
    
  5. Check if everything was removed from one's repository history, and that all branches are checked out. Only then move to the next step.

  6. Force-push the local changes to overwrite your repository on GitHub.com, as well as all the branches you've pushed up. A force push is required to remove sensitive data from your commit history. Read the first note at the bottom of this answer for more details one this.

    git push origin --force --all
    

Option 2

Using BFG Repo-Cleaner. This is faster and simpler than git filter-branch.

For example, to remove one's file with sensitive data and leave your latest commit untouched, run

bfg --delete-files YOUR-FILE-WITH-SENSITIVE-DATA

To replace all text listed in passwords.txt wherever it can be found in your repository's history, run

bfg --replace-text passwords.txt

After the sensitive data is removed, one must force push one's changes to GitHub.

git push --force

Additional

After using one of the options above:

  1. Contact GitHub Support.

  2. (If working with a team) Tell them to rebase, not merge, any branches they created off of one's old (tainted) repository history. One merge commit could reintroduce some or all of the tainted history that one just went to the trouble of purging.

  3. After some time has passed and you're confident that one had no unintended side effects, one can force all objects in one's local repository to be dereferenced and garbage collected with the following commands (using Git 1.8.5 or newer):

    git for-each-ref --format="delete %(refname)" refs/original | git update-ref --stdin
    
    git reflog expire --expire=now --all
    
    git gc --prune=now
    

Alternative

If the file was added with the most recent commit, and one has not pushed to GitHub.com, one can delete the file and amend the commit:

  1. Open GitBash and access the repository.

    cd YOUR-REPOSITORY.l
    
  2. To remove the file, enter git rm --cached:

    git rm --cached GIANT_FILE
    # Stage our giant file for removal, but leave it on disk
    
  3. Commit this change using --amend -CHEAD:

    git commit --amend -CHEAD
    # Amend the previous commit with your change
    # Simply making a new commit won't work, as you need
    # to remove the file from the unpushed history as well
    
  4. Push one's commits to GitHub.com:

    git push
    # Push our rewritten, smaller commit
    

For the Future

In order to prevent sensitive data to be exposed, other good practices include:

  • Use a visual program to commit the changes. There are various alternatives (such as GitHub Desktop, GitKraken, gitk, ...) and it could be easier to track the changes.

  • Avoid the catch-all commands git add . and git commit -a. Instead, use git add filename and git rm filename to individually stage files.

  • Use git add --interactive to individually review and stage changes within each file.

  • Use git diff --cached to review the changes that one has staged for commit. This is the exact diff that git commit will produce as long as one doesn't use the -a flag.

  • Generate Secret Keys in secure hardware (HSM boxes, hardware keys - like Yubikey / Solokey), that never leaves it.

  • Train the team on x508.


Notes:

Gonçalo Peres
  • 11,752
  • 3
  • 54
  • 83