GitGram hosts bare git repositories over HTTP. You can clone, push, and pull using standard git commands. No special software is needed beyond git itself.
git clone http://macrohardon.com/yourrepo.git
Pushing requires your username and password (set in data/users.json).
git remote add origin http://macrohardon.com/yourrepo.git git push origin main
Git will prompt for credentials. To cache them:
git config credential.helper store
SSH into your server and run:
cd /path/to/gitgram/repos git init --bare yourrepo.git echo "My new repository" > yourrepo.git/description
The repo will appear on the home page immediately.
cd my-project git init git add . git commit -m "Initial commit" git remote add origin http://macrohardon.com/yourrepo.git git push -u origin main
Generate a bcrypt hash:
php -r "echo password_hash('yourpassword', PASSWORD_DEFAULT);"
Paste the output into data/users.json:
{
"yourusername": {
"name": "Your Name",
"password_hash": "$2y$12$...",
"role": "admin"
}
}
If push/pull over HTTP fails, verify the backend is available:
which git-http-backend ls /usr/lib/git-core/git-http-backend
Update GIT_HTTP_BACKEND_PATHS in config.php if yours is in a different location.
If your host provides SSH access, you can also push over SSH directly to the bare repo path. No special GitGram configuration needed — just standard git+ssh.
git remote add ssh-origin ssh://user@host/path/to/repos/yourrepo.git git push ssh-origin main
GitGram uses a permission model modelled on
Gitolite.
Access rules live in data/repos.json — no database needed.
| Symbol | Meaning |
|---|---|
R | Read-only (clone, fetch) |
RW | Read + write (push) |
RW+ | Read + write + force-push / tag deletion |
- | Explicit deny (overrides any group grant) |
| Subject | Meaning |
|---|---|
@all | Everyone, including anonymous visitors |
@groupname | All users whose groups array includes groupname |
username | A specific user |
Priority (highest wins): explicit deny - > username > @group > @all.
Admin-role users always have full access regardless of rules.
data/repos.json{
"public-project": {
"description": "Anyone can read, devs can write",
"access": {
"@all": "R",
"@devs": "RW",
"alice": "RW+"
}
},
"private-project": {
"description": "Invite-only",
"access": {
"bob": "RW",
"charlie": "R"
}
},
"locked-repo": {
"description": "Archived — no pushes",
"access": {
"@all": "R",
"@devs": "R",
"alice": "-"
}
}
}
data/users.json with groups{
"alice": {
"name": "Alice",
"password_hash": "$2y$12$...",
"role": "admin",
"groups": ["devs", "leads"]
},
"bob": {
"name": "Bob",
"password_hash": "$2y$12$...",
"role": "user",
"groups": ["devs"]
},
"charlie": {
"name": "Charlie",
"password_hash": "$2y$12$...",
"role": "user",
"groups": []
}
}
Repos with no entry in repos.json are private (admin-only) by default —
the same safe default as Gitolite.
refs/heads/main); GitGram access is repo-level only