Using VS Code and Git CLI with Forgejo on Ubuntu

After moving more of my daily work to an Ubuntu workstation, one of the smaller questions was “how do I want to manage my Git repositories”

On Windows I used GitHub Desktop quite often. It provides a simple workflow for cloning repositories, reviewing changes, staging files, committing and pushing without having to think too much about the underlying Git commands.

My repositories however are mainly hosted on my self-hosted Forgejo instance, and on Linux the choice is slightly different.

I looked at dedicated Git clients such as GitKraken and a few alternatives, but eventually decided not to add another Git application at all.

The setup I ended up with is simply:

Ubuntu Workstation
        |
        +-- VS Code
        |     |
        |     +-- Source Control
        |     +-- Diff Viewer
        |     +-- Git Graph
        |     `-- Integrated Terminal
        |
        +-- Git CLI
        |
        `-- SSH
              |
              `-- Forgejo

For my use case this gives me almost everything I used GitHub Desktop for, while keeping normal Git available underneath.

Installing and Configuring Git

Git was already installed on my Ubuntu workstation:

git --version

which returned:

git version 2.53.0

I also checked my existing global configuration:

git config --global --list --show-origin

My basic identity was already configured:

user.name=Michael
user.email=forgejo@example.com

A few additional defaults are useful:

git config --global init.defaultBranch main
git config --global pull.rebase false
git config --global fetch.prune true
git config --global core.autocrlf input

The important one for Linux/Windows interoperability is:

core.autocrlf=input

Git will convert CRLF line endings to LF when committing but will not convert files back to CRLF when checking them out on Linux.

Using a Dedicated SSH Key for Forgejo

I prefer using separate SSH keys for different purposes instead of sharing one key between everything.

First I checked whether the workstation already contained SSH keys:

ls -la ~/.ssh

In my case there were no client keys yet, so I created a dedicated Ed25519 key for Forgejo:

ssh-keygen \
  -t ed25519 \
  -C "forgejo@example.com" \
  -f ~/.ssh/id_ed25519_forgejo

This creates:

~/.ssh/id_ed25519_forgejo
~/.ssh/id_ed25519_forgejo.pub

The .pub file is the public key and can be added to the Forgejo account.

The private key stays on the workstation.

To display the public key:

cat ~/.ssh/id_ed25519_forgejo.pub

I then added it in Forgejo under:

User Settings
    -> SSH / GPG Keys
        -> Add Key

Creating an SSH Alias

Instead of remembering the IP address, port and SSH identity for every repository, I added an entry to:

~/.ssh/config

Example:

Host forgejo
    HostName 192.168.10.10
    Port 30143
    User git
    IdentityFile ~/.ssh/id_ed25519_forgejo
    IdentitiesOnly yes

I also restricted the configuration file permissions:

chmod 600 ~/.ssh/config

The effective configuration can be checked with:

ssh -G forgejo | grep -E '^(hostname|user|port|identityfile) '

This should show something similar to:

hostname 192.168.10.10
user git
port 30143
identityfile ~/.ssh/id_ed25519_forgejo

The connection itself can then be tested with:

ssh -T forgejo

The alias is useful because it also makes Git repository URLs much cleaner.

Instead of:

ssh://git@192.168.10.10:30143/Michael/k8s-gitops.git

I can use:

forgejo:Michael/k8s-gitops.git

Keeping Repositories in One Directory

I created a simple directory for my local Git repositories:

mkdir -p ~/git-repos
cd ~/git-repos

From there I cloned one of my existing Forgejo repositories:

git clone forgejo:Michael/k8s-gitops.git

Git successfully cloned the repository:

Cloning into 'k8s-gitops' ...
Receiving objects: 100%
Resolving deltas: 100%

Afterwards I verified the repository:

cd k8s-gitops

git status
git branch --show-current
git remote -v

The result should look similar to:

On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

main

origin  forgejo:Michael/k8s-gitops.git (fetch)
origin  forgejo:Michael/k8s-gitops.git (push)

The important part here is the remote:

forgejo:Michael/k8s-gitops.git

Git automatically resolves forgejo through ~/.ssh/config.

Opening the Repository in VS Code

From the repository directory:

code .

VS Code automatically detects that the directory contains a Git repository.

On the first opening I ran into one more small detail: VS Code opened the directory in Restricted Mode.

The Source Control view showed:

None of the registered source control providers work in Restricted Mode.

Since this is my own local repository, I selected:

Manage Workspace Trust

and trusted the folder.

Afterwards VS Code immediately enabled its Git integration.

The Source Control pane now showed:

CHANGES
Commit Message
Commit

and the Git Graph displayed the existing commit history and the current main branch.

No additional Git extension was required.

A Few VS Code Git Settings

For now I keep the Git setup intentionally simple.

The settings I use are:

Git: Autofetch             Enabled
Git: Confirm Sync          Enabled
Git: Enable Smart Commit   Disabled

Autofetch

Autofetch lets VS Code periodically fetch remote changes.

This does not modify my working tree like a pull would. It only updates the knowledge about the remote repository.

Confirm Sync

I leave confirmation enabled so that synchronization does not happen accidentally.

Smart Commit

I disable Smart Commit.

I prefer explicitly selecting and staging the files that belong to a commit rather than letting VS Code automatically include all modified files.

This is especially useful for infrastructure repositories where a working directory can easily contain several unrelated YAML, Terraform or documentation changes.

VS Code and Git CLI Together

One reason I like this setup is that VS Code does not hide Git.

I can use the graphical interface when it is convenient and immediately switch to the integrated terminal when I want more detail.

My normal workflow looks roughly like this:

git pull
    |
    v
Edit files in VS Code
    |
    v
Review Diff
    |
    v
Stage selected files
    |
    v
Commit
    |
    v
Push to Forgejo

Before starting work:

git status
git pull

While working:

git diff

After committing:

git log -5 --oneline
git status

For day-to-day commits I can use the VS Code Source Control interface.

For troubleshooting or more advanced Git operations I still have the full Git CLI directly underneath it.

The Final Setup

The resulting setup is surprisingly small:

Ubuntu
|
+-- ~/git-repos/
|   |
|   +-- k8s-gitops/
|   +-- k8s-infra/
|   +-- rancher-mgmt/
|   `-- ...
|
+-- Git CLI
|
+-- VS Code
|
`-- ~/.ssh/
    |
    +-- config
    +-- id_ed25519_forgejo
    `-- id_ed25519_forgejo.pub

Forgejo remains a completely normal Git remote.

There is no Forgejo-specific client required and no dependency on GitHub Desktop.

Final Thoughts

This started as a search for a Linux replacement for GitHub Desktop.

In the end I decided that I did not actually need one.

VS Code already gives me the visual parts I liked about GitHub Desktop: file changes, diffs, staging, commits and repository history.

For me the combination is actually a better fit for an Ubuntu workstation:

VS Code for convenience.
Git CLI for control.
SSH for authentication.
Forgejo for hosting.

It also has another useful side effect: instead of learning how a specific Git client represents Git operations, I continue learning Git itself.