DEV Community

Wen Ping
Wen Ping

Posted on Edited on Originally published at anguskit.com

Move a GitHub project to an internal Git host: import or push, then follow upstream

This article explains how to move a GitHub project onto an intranet AngusGit, whether to import or to push, and how to keep following upstream afterward. After reading it, you can choose by asking whether you will still follow upstream: if you will not, and you only need the current code, use Import repository; if you need the history or you will merge upstream, clone locally, set GitHub as upstream, set a new empty repository as origin, and push, keeping in mind that an imported repository shares no history with GitHub and that merging upstream later reports refusing to merge unrelated histories.

Sign in to AngusGit first. Import is Repositories in the sidebar, then Import: Repository URL is the HTTPS address, Owner is the current account or a team, and you type Repository name yourself. Turn on Make this repository private when only the team should see it, then click Begin import. Prepare credentials before you push. For SSH, if you have no ~/.ssh/id_ed25519.pub, run ssh-keygen -t ed25519 -C "you@example.com", press Enter for the path, and register the line from cat ~/.ssh/id_ed25519.pub that starts with ssh-ed25519 under Settings → Profile, on the SSH Keys tab. The username is git, and the port is 2222. For HTTPS, an access token is a secret string you generate and use in place of a password. Open the Access Tokens tab, click Generate New Token, enter your current login password, select git:write, set the repository permission to WRITE, and copy the token immediately. The username is your login name, and the password is the token.

The import path in this article uses https://github.com/octocat/Hello-World.git and ends as the private repository xlliu/hello-world-import. The push path gives the full commands. Create the target as an empty repository on AngusGit before you push. anguskit/AngusRepo is already on the instance. It belongs to the artifact-repository product and is unrelated to this import.

1. Choose the path first

Read the table from the top. First check whether the source can be read anonymously. Visibility decides who can see a repository, and a private one requires a sign-in. Then decide whether you need the history and whether you will follow upstream. Use import only when the first three rows do not apply.

What you need Which path Why
The source repository is private on GitHub Push Import fetches anonymously and cannot read a private repository
Keep the original commit history, so git blame and debugging still work Push Import brings the files on the default branch and leaves the history behind
Merge new upstream versions later Push Merging upstream needs shared history, and an imported repository has none
Only the current code, maintained by you from now on, with no further upstream Import Fastest. One address finishes it, with no local steps

Teams that build on an open-source project usually want upstream security fixes and new features a few months later. When you are unsure, push. The extra time is a few minutes. Putting the history back after an import means starting over.

Push when the source requires a sign-in. For a public repository, then decide whether you need the history

2. Current code only: import

In the sidebar, click Repositories, then Import at the top right. For Hello-World, set the source URL to https://github.com/octocat/Hello-World.git, the owner to Angus / xlliu, and the name to hello-world-import. Turn on Make this repository private, then click Begin import. The result is one branch, main, and one commit, Initial commit from https://github.com/octocat/Hello-World.git. The only file is README. The source default branch was master. After import the branch name is main. Fill the form with the fields from the start of this article.

The AngusGit server does the fetch. Your own computer does not. Hello-World could be imported because that server could reach github.com at the time. If AngusGit runs on an intranet and you are not sure it can reach GitHub, run this on that server first:

git ls-remote https://github.com/octocat/Hello-World.git
Enter fullscreen mode Exit fullscreen mode

If it prints branch names and commit ids, the server can reach GitHub. If the command hangs or reports a connection failure, import fails too. Use section 3: clone on a computer that can reach GitHub, then push to AngusGit.

3. History, and following upstream: push

On this path you clone the GitHub repository in full on your computer, then push it to a new empty repository on AngusGit. Keep this local clone. Every later upstream sync happens inside it.

  1. Create an empty repository on AngusGit In the sidebar, click Repositories, then New repository. Leave Add a README file, Add .gitignore, and Choose a license unchecked. That is what makes the repository empty. Any checked box adds a commit, and the push is then rejected because the histories do not match. Do not use an existing repository as the target. That includes the imported xlliu/hello-world-import and the existing anguskit/AngusRepo.
  2. Clone from GitHub Use a machine that can reach both GitHub and the intranet AngusGit, and clone the source there.
  3. Rename GitHub to upstream and add AngusGit After the clone, origin points at GitHub. Rename it to upstream, then add the empty AngusGit repository as the new origin. Day-to-day fetch and push then use origin. upstream is only for syncing upstream. git remote -v should show two pairs: origin is AngusGit, and upstream is GitHub.
  4. Push the main branch and the tags Hello-World's upstream branch is master, and a new empty repository on AngusGit uses main. Run git branch -m master main, then push main and every tag. Skip the rename when the source main branch is already main.

The commands for those steps are below. xlliu/hello-world-history is only an example name. Create it as an empty repository on AngusGit before you push.

git clone https://github.com/octocat/Hello-World.git hello-world-history
cd hello-world-history
git remote rename origin upstream
git branch -m master main
git remote add origin ssh://git@git.anguskit.com:2222/xlliu/hello-world-history.git
git remote -v
git push -u origin main
git push origin --tags
Enter fullscreen mode Exit fullscreen mode

After the push, the commit history on AngusGit should match GitHub, and Tags should show the upstream versions. You create hello-world-history yourself. That repository is not on the instance yet.

The commands above push only main. Other upstream branches, such as release-1.x, stay on your computer as remote-tracking branches. A remote-tracking branch is a local pointer such as upstream/release-1.x that records where the upstream branch is. It has not been pushed to AngusGit. Push one when you need it:

git push origin upstream/release-1.x:refs/heads/release-1.x
Enter fullscreen mode Exit fullscreen mode

The example uses an SSH URL. An HTTPS URL works too. In the password field, paste the git:write access token from the start of this article. Register the SSH public key as at the start as well.

upstream is only for fetching from GitHub. origin points at an empty repository you create first

4. How to sync upstream later

When upstream publishes a new version, bring it in with the commands below. Merge on a sync branch, then open a pull request into main. A pull request is a request to merge one branch into another after someone reviews the change. Two things follow from that: the sync is reviewed like any other change, and a protected main does not reject you for pushing straight to it. For Hello-World the upstream branch is master, so the merge is upstream/master. When the source main branch is main, change the third line to git merge upstream/main.

git fetch upstream
git switch -c sync-upstream-2026-10 main
git merge upstream/master
git push -u origin sync-upstream-2026-10
Enter fullscreen mode Exit fullscreen mode

The four lines do this, in order. The first fetches new commits from GitHub and changes no branch. The second creates the sync branch sync-upstream-2026-10 from main. The third merges the new upstream commits. If a file you changed internally was also changed upstream, this step reports a conflict. Edit the conflicted files, then run git add and git commit. The last line pushes the sync branch to AngusGit.

After the push, open a pull request on AngusGit from the sync branch into main. Merge it after review. This flow works for a repository you pushed. xlliu/hello-world-import has only the initial commit and shares no history with GitHub. git merge upstream/main on that repository returns refusing to merge unrelated histories.

Do not wait too long between syncs. Hundreds of upstream commits in one merge pile the conflicts together. Sync on upstream's release cadence, and each merge has fewer conflicts.

5. Which files in .github still do something

GitHub repositories often contain a .github directory. Import and push both leave those files in the repository. What they do on AngusGit differs:

File Does it take effect on AngusGit? Notes
.github/workflows/*.yml (GitHub Actions) Does not run automatically Pipelines read .anguscicd.yaml or .anguscicd/
.github/CODEOWNERS Is read Turn on Enable CODEOWNERS first
.github/PULL_REQUEST_TEMPLATE.md Is read when you create a pull request With no such file, the description stays empty
.github/ISSUE_TEMPLATE Is not used as an issue template AngusGit does not read this directory

A few details sit behind that table. GitHub Actions workflow files do not run by themselves. You can import them in the console and convert them into an AngusGit pipeline. The linked article at the end shows how. CODEOWNERS is a file that names an owner for each path. After you turn on Enable CODEOWNERS under Settings → Pull Requests, AngusGit assigns reviewers from those paths. It also reads CODEOWNERS in the repository root and docs/CODEOWNERS, and it only understands @username entries. The pull request template is also read from the root and from docs/ under the same file name. Issue templates live in AngusGit's own template data.

Issues, pull requests, Releases, and Wiki on GitHub are not stored inside the Git repository, so import and push leave them behind.

6. The team's own private GitHub repositories

A private repository the team owns on GitHub cannot be imported. Push it. When you clone a private GitHub repository, the password field needs a GitHub personal access token. GitHub does not accept the account password there.

If you will not sync with GitHub after the move, you do not need to keep upstream. git clone --bare is simpler. It creates a bare repository: Git data only, meaning every branch, tag, and commit, with no working files to edit. You can push every branch and tag in one pass:

git clone --bare https://github.com/<your-org>/<repo>.git
cd <repo>.git
git push ssh://git@git.anguskit.com:2222/<owner>/<repo>.git --all
git push ssh://git@git.anguskit.com:2222/<owner>/<repo>.git --tags
Enter fullscreen mode Exit fullscreen mode

Replace the owner and repository name with the empty repository you created on AngusGit. Do not push to anguskit/AngusKit or anguskit/AngusRepo. Those already have content.

After the push, each person clones a fresh copy from AngusGit. Or, in the old local repository, run git remote set-url origin <AngusGit URL> and switch the remote.

7. When something fails

What you see Cause What to do
fatal: refusing to merge unrelated histories The AngusGit repository was created with Import repository and shares no history with upstream Create an empty repository and push again as in section 3. After you confirm nobody still uses the imported repository, delete it
! [rejected] main -> main (fetch first) during push The AngusGit repository is not empty. A common cause is checking Add a README file at creation Delete it and create a new empty repository with all three boxes unchecked
Source repository URL "…" is invalid or inaccessible during import The repository is private, or the AngusGit server cannot reach GitHub Run git ls-remote on the server to check the network. For a private repository, or when the network is blocked, clone locally and push as in section 3
git merge upstream/main reports conflicts An internal edit and an upstream edit changed the same place Open the conflicted files, keep the content you need, then git add and git commit. When there are many conflicts, resolve them on the sync branch, then open the pull request
The merge card says a review is required main has branch protection, or the repository requires code review This is expected. The reviewer approves the pull request, then you merge

Common questions

Can I add history to a repository that was already imported?

The clean way is to create an empty repository, push as in section 3, and have the team switch to the new repository. An imported repository has one commit. Forcing history onto it tends to scramble everyone's local copy.

Do upstream commit authors show up on AngusGit?

On the push path, yes. The history is copied as it is. Each commit's author, time, and message match GitHub. On the import path, no. The new repository has one commit you made, Initial commit from ….

Does every teammate configure upstream?

No. One person syncs upstream, pushes the sync branch to AngusGit, and finishes the pull request. Everyone else pulls from origin as usual.

What if the source uses Git LFS?

AngusGit does not support Git LFS. Git LFS (Git Large File Storage) stores large files outside normal Git history. After the push, those files exist on AngusGit only as pointer files. Commit them as normal files, or store them in an artifact repository.

Summary

When you move a repository from GitHub, first ask whether you will still follow upstream. If you will not, and you only need the current files, use Import repository. That is how xlliu/hello-world-import was created. To keep the history, or to merge upstream later, git clone locally, rename GitHub to upstream, and add a new empty repository as origin. Push and sync only to that new empty repository. Leave the imported repository out of the push.

Conclusion: When you still need to merge GitHub upstream updates, or you need to keep commit history, skip Import repository. git clone locally, set GitHub as upstream, set an empty AngusGit repository as origin, and push.

Further reading

Top comments (0)