Project

General

Profile

Actions

Git Workflow » History » Revision 1

Revision 1/11 | Next »
Redmine Admin, 10/25/2026 04:16 PM


Git Workflow (/srv/git, read-only into Redmine)

Repos are bare, owned by the restricted host user git (git-shell) — the one

deliberate exception to the no-host-packages rule. Redmine only reads them.

Who does what — this is the part that causes confusion, so read it first:

Step Run as Why
create bare repo, ACL, SELinux label root /srv/git is root/git territory
git add / git commit in a working copy owner user (websvcmaster) keeps .git/objects owned by that user
commit + push, day to day root, via deploy-commit.sh it commits as websvcmaster and pushes as root itself — the only command you normally type
git push by hand root push needs sudo -u git git-receive-pack; websvcmaster has no sudo and never will
register repo in Redmine Redmine admin UI

websvcmaster's password is locked on purpose (passwd -l, set by

01-bootstrap-root.sh). Enter it only via su - websvcmaster from root — never

set a password for it. If a command run as websvcmaster asks for a password, it

is a root command run in the wrong shell: exit to root and run it there.

(passwd -S websvcmaster must show L.)

Repos: mds-dark.git (WordPress theme, project "MDS / MIMMAR Web Page Design"),

dmz-web-deploy.git (this host's ~/deploy, project "dmz-web Infrastructure").

How Redmine reads them

# site-redmine.container
Volume=/srv/git:/srv/git:ro
Environment=GIT_CONFIG_COUNT=1
Environment=GIT_CONFIG_KEY_0=safe.directory
Environment=GIT_CONFIG_VALUE_0=*

Rootless UID mapping: container uid n → host uid 200000 + n − 1; Redmine

runs as uid 999 → host 200998 (podman top site-redmine user huser).

One-time host prep (root):

semanage fcontext -a -t container_file_t '/srv/git(/.*)?'
restorecon -Rv /srv/git

A. New bare repo — root

R=/srv/git/<repo>.git
sudo -u git git init --bare $R
sudo -u git git -C $R symbolic-ref HEAD refs/heads/main   # bare HEAD defaults to master; every repo here uses main
setfacl -R  -m u:200998:rX $R                             # Redmine read access
setfacl -R -d -m u:200998:rX $R                           # ...also for files future pushes create
restorecon -Rv $R      # "not reset as customized by admin" on every file = already container_file_t = OK
git config --global --add safe.directory /home/<user>/<workdir>   # lets root push from that working copy

B. Working copy, first time — owner user (su - <user> from root)

git config --global user.name  "<user>"                   # once per user, or commit refuses
git config --global user.email "sysadmin@mdsdefense.com"  # matching a Redmine user's email links commits to them
cd <workdir>
git init -b main
# .gitignore first: no *.env, *secret*, *password*, *.key/*.pem/*.crt, dumps, archives, data/, tmp/
git add -A && git status --short
grep -nEi 'password|secret|token|key_base' -r . --exclude-dir=.git | grep -viE 'Secret=|podman secret|\$\{?[A-Z_]*(PASSWORD|SECRET)'   # must be comments/prompts only
git commit -m "initial import $(date +%F)"
git remote add origin /srv/git/<repo>.git
git log --oneline
exit                                                      # back to root

C. Push — root

First push of a new working copy (once):

cd /home/<user>/<workdir>
git push --receive-pack='sudo -u git git-receive-pack' -u origin main
chown -R <user>:<user> .git          # root's push leaves root-owned files in .git

Expected: * [new branch] main -> main. Verify: sudo -u git git -C /srv/git/<repo>.git log --oneline.

After that, never push by hand — use deploy-commit.sh (E).

D. Redmine

Project → Settings → Repositories → New repository: SCM Git, Main repository

✓, Identifier <repo>, Path /srv/git/<repo>.git, Report last commit ✓ → Create.

Open the Repository tab; the commit must be listed. If not:

podman exec site-redmine git -C /srv/git/<repo>.git log --oneline —

"dubious ownership" → safe.directory env missing; "permission denied" → ACL (step A).

E. Day-to-day — deploy-commit.sh (root)

One root command does the whole cycle in the right identities: git add -A

and git commit as websvcmaster (identity auto-set), a guard that refuses

to commit staged lines that look like literal credentials, git push

as root via sudo -u git git-receive-pack, optional annotated tag, and

chown of .git back to websvcmaster. Source: root-bin/deploy-commit.sh in

the repo; installed as /usr/local/sbin/deploy-commit.sh (0750).

deploy-commit.sh -n                         # dry run: shows what is staged, commits nothing
deploy-commit.sh -m "what changed"          # commit + push
deploy-commit.sh -m "cheat sheet v1.1" -t cheatsheet-1.1   # ...+ tag
deploy-commit.sh -t cheatsheet-1.1          # nothing to commit: tag HEAD, push the tag
deploy-commit.sh -y -m "..."                # skip the credential guard after checking the flagged lines
deploy-commit.sh -h

Editing still happens as websvcmaster (su - websvcmaster, edit, exit);

deploy-commit.sh never edits files, only .git. The guard flags staged lines of

the form <…password|secret|token|api_key|app_key…> = <literal of 8+ chars>;

variables ($X), placeholders (<…>, ...), Quadlet Secret= lines and

comments are ignored. If it fires: move the value to a Podman secret or

.gitignore the file and re-run; if the lines are harmless, re-run with -y. Install/update the script:

install -o root -g root -m 0750 /home/websvcmaster/deploy/root-bin/deploy-commit.sh /usr/local/sbin/deploy-commit.sh

Recovering from the usual mistakes

  • fatal: could not open '.git/COMMIT_EDITMSG': Permission denied (or any

permission error on commit) → root touched .git (committed there, or pushed/tagged

without deploy-commit.sh). Fix: chown -R <user>:<user> /home/<user>/<workdir>/.git,

then use deploy-commit.sh from now on (it chowns every time). Also check root's ~/.gitconfig didn't pick up the user's identity.

  • Everything up-to-date on push right after you expected a new commit → the

commit didn't happen (identity error or permission error scrolled past); git log --oneline

and git status --short in the working copy. A tag made at that moment points at the

old commit: deploy-commit.sh -t <tag> after the real commit (it force-moves the tag).

  • error: src refspec main does not match any → no commit yet on main (step B not done).
  • error: key does not contain a section: receivepack → wrong syntax; use
    --receive-pack= (step C), not -c receivepack=.
  • sudo: a password is required / password prompt → you are websvcmaster; this is a root step (exit, then deploy-commit.sh).
  • Redmine shows the repo empty → bare HEAD still points to master (step A symbolic-ref).

Source: dmz-web-podman-cheatsheet.md v1.1.1 in the dmz-web-deploy repo (Releases). Edit the wiki and the repo copy together.
Git Workflow (/srv/git, read-only into Redmine)
How Redmine reads themA. New bare repo — rootB. Working copy, first time — owner user (su - from root)C. Push — rootD. RedmineE. Day-to-day — deploy-commit.sh (root)Recovering from the usual mistakes
☰

Updated by Redmine Admin 12 days ago · 11 revisions