iTranslated by AI
How to Fork a Public GitHub Repository to a Private One
I want to fork a Git repository managed publicly by someone else for personal use (or as an internal-only project).
When searching online, there are various methods and the differences aren't very clear, so I decided to test them out.
Quick Conclusion First
If you want to copy everything, including branches, just clone with the --mirror flag and then push to your own private repository with the --mirror flag.
git clone --mirror https://github.com/${user-or-org}/${repo-fork-from}.git
git push --mirror https://github.com/${user-or-org}/${repo-fork-to}.git
That's it.
As a side note, make sure to disable GitHub Actions in the settings of your forked repository. If the source repository has workflow files, they might be triggered without you realizing it, which can be a surprise.
Creating the Source Repository
First, I'll create the source repository -> https://github.com/hatappo/fork-test
- Create the repository and push
- Create a branch
feature/fuga, push it, and merge it intomainon GitHub
We will start from this state.
Trying to Fork
I'll try four patterns.
- a) Official GitHub Fork
- b) Simple clone, add a remote, and push.
- c)
clone --bareand push. - d)
clone --mirrorandpush --mirror.
a) Official GitHub Fork
From the GitHub interface, click "Fork" -> "Create a new fork".
Here, I learned that "you cannot fork a repository you own to your own account." That makes sense. I'll give up on this.
Regardless, this method doesn't achieve the goal of making it "Private," so it's fine.
b) Simple clone, add a remote, and push.
# Checkout with the name fork-test-simple
$ git clone https://github.com/hatappo/fork-test.git fork-test-simple
Cloning into 'fork-test-simple'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 7 (delta 1), reused 5 (delta 0), pack-reused 0
Receiving objects: 100% (7/7), done.
Resolving deltas: 100% (1/1), done.
$ cd ./fork-test-simple
# At this point, create the destination repository fork-test-simple on GitHub.
# Add the remote repository and push
$ git remote add my https://github.com/hatappo/fork-test-simple.git
$ git push -u my main
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 856 bytes | 856.00 KiB/s, done.
Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test-simple.git
* [new branch] main -> main
branch 'main' set up to track 'my/main'.
Created repository → https://github.com/hatappo/fork-test-simple
-
Branch: Only the default branch
mainwas copied.
c) clone --bare and push.
# Checkout with the name fork-test-bare
$ git clone --bare https://github.com/hatappo/fork-test.git fork-test-bare
Cloning into bare repository 'fork-test-bare'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 7 (delta 1), reused 5 (delta 0), pack-reused 0
Receiving objects: 100% (7/7), done.
Resolving deltas: 100% (1/1), done.
$ cd ./fork-test-bare
# At this point, create the destination repository fork-test-bare on GitHub.
# Add the remote repository and push
$ git remote add my https://github.com/hatappo/fork-test-bare.git
$ git push -u my main
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 856 bytes | 856.00 KiB/s, done.
Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test-bare.git
* [new branch] main -> main
branch 'main' set up to track 'my/main'.
Created repository → https://github.com/hatappo/fork-test-bare
-
Branch: Only the default branch
mainwas copied.
d) clone --mirror and push --mirror.
# Checkout with the name fork-test-mirror
$ git clone --mirror https://github.com/hatappo/fork-test.git fork-test-mirror
Cloning into bare repository 'fork-test-mirror'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 7 (delta 1), reused 5 (delta 0), pack-reused 0
Receiving objects: 100% (7/7), done.
Resolving deltas: 100% (1/1), done.
$ cd ./fork-test-mirror
# Push with --mirror
$ git push --mirror https://github.com/hatappo/fork-test-mirror.git
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (7/7), 922 bytes | 922.00 KiB/s, done.
Total 7 (delta 1), reused 7 (delta 1), pack-reused 0
remote: Resolving deltas: 100% (1/1), done.
To https://github.com/hatappo/fork-test-mirror.git
* [new branch] feature/fuga -> feature/fuga
* [new branch] main -> main
! [remote rejected] refs/pull/1/head -> refs/pull/1/head (deny updating a hidden ref)
error: failed to push some refs to 'https://github.com/hatappo/fork-test-mirror.git'
An error appears at the end...
Created repository → https://github.com/hatappo/fork-test-mirror
- Branch: All branches were copied.
Merging Subsequent Updates from the Source Repository into the Forked Repository
Assuming the local repository used earlier was deleted, this is a scenario for how to incorporate updates again.
b) Standard Pattern
# Clone the forked repository
$ git clone https://github.com/hatappo/fork-test-simple
Cloning into 'fork-test-simple'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), done.
# Pull from origin (source)
cd ./fork-test-simple
$ git pull origin main
From https://github.com/hatappo/fork-test
* branch main -> FETCH_HEAD
Updating be26448..2a3622c
Fast-forward
README.md | 4 ++++
1 file changed, 4 insertions(+)
# Push to my (destination)
$ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 665 bytes | 665.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test-simple
be26448..2a3622c main -> main
Clone the forked repository, pull from the source, and push to the forked destination. It's quite straightforward.
c) --bare Pattern
# Clone the source repository
$ git clone --bare https://github.com/hatappo/fork-test fork-test-bare
Cloning into bare repository 'fork-test.git'...
remote: Enumerating objects: 11, done.
remote: Counting objects: 100% (11/11), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 11 (delta 2), reused 7 (delta 0), pack-reused 0
Receiving objects: 100% (11/11), done.
Resolving deltas: 100% (2/2), done.
$ cd ./fork-test-bare
# Add forked repository as a remote and push
$ git remote add my https://github.com/hatappo/fork-test-bare.git
$ git push -u my main
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 665 bytes | 665.00 KiB/s, done.
Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test-bare.git
be26448..2a3622c main -> main
branch 'main' set up to track 'my/main'.
I couldn't figure out how to pull from the source if I cloned the forked repository. So, I cloned the source repository and followed the same steps as before.
d) --mirror Pattern
# Clone the source repository
$ git clone --mirror https://github.com/hatappo/fork-test fork-test-mirror
Cloning into bare repository 'fork-test-mirror'...
remote: Enumerating objects: 11, done.
remote: Counting objects: 100% (11/11), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 11 (delta 2), reused 7 (delta 0), pack-reused 0
Receiving objects: 100% (11/11), done.
Resolving deltas: 100% (2/2), done.
$ cd ./fork-test-mirror
# Add forked repository as a remote and push
$ git remote add my https://github.com/hatappo/fork-test-mirror.git
$ git push --mirror my
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 278 bytes | 278.00 KiB/s, done.
Total 3 (delta 0), reused 2 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test-mirror.git
* [new branch] feature/piyo -> feature/piyo
* [new reference] my/main -> my/main
! [remote rejected] refs/pull/1/head -> refs/pull/1/head (deny updating a hidden ref)
! [remote rejected] refs/pull/2/head -> refs/pull/2/head (deny updating a hidden ref)
error: failed to push some refs to 'https://github.com/hatappo/fork-test-mirror.git'
Again, I cloned the source repository and followed the same steps as the initial process. The difference remains that even branches are copied.
To resolve the error at the end, see here -> https://tsuyoshin.hatenablog.com/entry/20200930/1601424899
Conclusion
- I didn't really see a difference between using
--bareand not using it. - If you want to copy everything including branches (and probably tags, although I didn't test them), simply use
--mirrorfor both cloning and pushing.
Bonus - Creating the Source Repository
$ mkdir fork-test
$ cd fork-test/
$ git init
Initialized empty Git repository in /*****/fork-test/.git/
# Create file
$ git add --all && git commit --message "1st commit"
[main (root-commit) 05e5fa4] 1st commit
1 file changed, 2 insertions(+)
create mode 100644 README.md
# Create a repository on GitHub using the gh command and push
$ gh repo create
? What would you like to do? Push an existing local repository to GitHub
? Path to local repository .
? Repository name fork-test
? Repository owner hatappo
? Description
? Visibility Public
✓ Created repository hatappo/fork-test on GitHub
? Add a remote? Yes
? What should the new remote be called? origin
✓ Added remote https://github.com/hatappo/fork-test.git
? Would you like to push commits from the current branch to "origin"? Yes
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 231 bytes | 231.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/hatappo/fork-test.git
* [new branch] HEAD -> main
branch 'main' set up to track 'origin/main'.
✓ Pushed commits to https://github.com/hatappo/fork-test.git
# Create a branch
$ git checkout -B feature/fuga
Switched to a new branch 'feature/fuga'
# Append to file
$ echo "\n\nfuga\nfuga" >> README.md
# Commit
$ git add --all && git commit --message "2nd commit"
[feature/fuga 75ddefa] 2nd commit
1 file changed, 4 insertions(+)
# Push the branch to GitHub
$ git push --set-upstream origin feature/fuga
...
remote:
remote: Create a pull request for 'feature/fuga' on GitHub by visiting:
remote: https://github.com/hatappo/fork-test/pull/new/feature/fuga
remote:
To https://github.com/hatappo/fork-test.git
* [new branch] feature/fuga -> feature/fuga
branch 'feature/fuga' set up to track 'origin/feature/fuga'.
Then, I created a PR on GitHub and merged feature/fuga into main.
The resulting GitHub repository → https://github.com/hatappo/fork-test
Discussion