summaryrefslogtreecommitdiff
path: root/git-repo
blob: 6e910bc2b13e812debda17495bd4b51e45613407 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#!/usr/bin/env bash
BASEDIR=/git

C_bg_green=$(echo -e "\e[42m")
C_bg_magenta=$(echo -e "\e[45m")
C_bg_default=$(echo -e "\e[49m")
C_bg_lblue=$(echo -e "\e[104m")

_getent() {
    local db=$1
    local key=$2
    getent $db $key | cut -d ':' -f 1
}

_list_repo_dirs() {
    find "$1" -maxdepth 6 -type d -not -path '*.git/*' -name '*.git' 2>/dev/null
}

do_list() {
    if [ -n "$1" ]; then
	local root_path=$(readlink -f "$1")
    else
	local root_path=$BASEDIR
    fi
    echo -n "Working.. listing repositories under $root_path"
    for repo_path in $(_list_repo_dirs $root_path | sort); do
        echo -ne "\r" # to remove working
	eval $(stat --format 'local repo_path_uid=%u repo_path_gid=%g repo_path_mode=%a' "$repo_path")
        if [ "$repo_path_uid" != "$UID" ]; then
            # so there must be a matching gid
            local pass=false
            for user_gid in $(id -G); do
                if [ "$repo_path_gid" == "$user_gid" ]; then
                    pass=true
                fi
            done
            if ! $pass; then
                continue # skip that repo since uid and gids don't match
            fi

        fi
	
        local shortdesc=$(cut -c 1-40 < "$repo_path/description")
	local permissions="owner=$(_getent passwd $repo_path_uid)"
	local color="$C_bg_green"
        if [ $(( 0$repo_path_mode & 070 )) -gt 0 ]; then # shared
	    permissions="$permissions group=$(_getent group $repo_path_gid)"
	    color="$C_bg_lblue"
	fi
        if [ $(( 0$repo_path_mode & 07 )) -gt 0 ]; then # public
	    permissions="$permissions PUBLIC"
            if ! [ -f "$repo_path/PUBLIC" ]; then
                permissions="${permissions}-but-cgit-marker-file-missing"
            fi
	    color="$C_bg_magenta"
	fi
	printf "%s%-60s (%s) (%s)\n" "$color" "$repo_path$C_bg_default" "$permissions" "$shortdesc"
    done
}

_create_repo() {
    local label=$1
    local shared=$2
    local group=$3
    local is_public=$4

    read -p 'New repository name.git: '
    local repo_name=$REPLY

    if ! `expr "$repo_name" : '.*\.git' >/dev/null`; then
	echo "Repository name must end with .git"
	return 1
    fi

    local repo_dir=$BASEDIR/$USER/${repo_name}

    test \! -e "$repo_dir" || { echo "Repo $repo_dir already exist"; exit 1; }

    read -p 'Set Description:         '
    local repo_desc=$REPLY

    read -p "Create $label in $repo_dir Ok? (y/N)"
    test "$REPLY" == "y" || exit 1

    mkdir -p "$repo_dir"
    git init -q --bare --shared=$shared "$repo_dir"
    git config receive.denyNonFastforwards false
    chgrp -R "$group" "$repo_dir"
    echo "$repo_desc" > "$repo_dir/description"

    if $is_public; then
	touch "$repo_dir/PUBLIC"
	echo "created $repo_dir/PUBLIC to expose via cgit"
    fi

    echo "done creating $label in $repo_dir"
    echo "use 'git repo show $repo_dir' for details"
    return 0
}

do_create_public() {
    _create_repo "public repository" "0664" "share" true
    return $?
}

do_create_shared() {
    _create_repo "group-writeable repository" "0660" "share" false
    return $?
}

do_create_private() {
    _create_repo "private repository" "0600" "$USER" false
    return $?
}

do_show() {
    for repo_path in $*; do
	if ! [ -f "$repo_path/HEAD" ]; then
	    repo_path="$BASEDIR/$repo_path"
	fi
        if ! [ -f "$repo_path/HEAD" ]; then
            echo "Not a git repository: $repo_path"
            continue
        fi

        local repo_file_mode=$(stat --format %a "$repo_path")
        local repo_file_uid=$(stat --format %u "$repo_path")
        local repo_file_user=$(_getent passwd $repo_file_uid)
        local repo_file_gid=$(stat --format %g "$repo_path")
        local repo_file_group=$(_getent group $repo_file_gid)
        local repo_git_shared=$(GIT_DIR="$repo_path" git config --get core.sharedrepository)

	echo "  Directory: $repo_path"
        echo "  ✔ Permissions mode ${repo_file_mode} (uid=${repo_file_uid}/${repo_file_user} gid=${repo_file_gid}/${repo_file_group})"
	echo "    git core.sharedrepository=$repo_git_shared"
        echo " ⚡ Clone read/write SSH: git clone 'ssh://localnet.cc${repo_path}'"
        if [ $(( 0$repo_file_mode & 07 )) -gt 0 ]; then
	    echo "    Clone read/-  HTTP:   git clone 'http://localnet.cc/cgit/cgit.cgi/${repo_path#$BASEDIR/}'"
	fi
	echo "    Update remote:        git remote set-url origin 'ssh://localnet.cc${repo_path}"
        echo "  ☛ To update description execute: $EDITOR $repo_path/description"
	echo ""
    done
}

do_mirror() {
    local root_path=$(readlink -f "$1")
    for repo_path in $(_list_repo_dirs $root_path); do
	local is_mirror=$(GIT_DIR="$repo_path" git config --get remote.origin.mirror)
	if [ "$is_mirror" != "true" ]; then
	    continue # skip non-mirror repo
	fi
	echo "Mirror $repo_path from $(GIT_DIR="$repo_path" git config --get remote.origin.url)"
	GIT_DIR="$repo_path" git fetch --force --prune origin
    done
}

do_make_public() {
    local repo_path=$1
    test -f "$repo_path/HEAD" || { echo "$repo_path is not a git repository"; exit 1; }
    set -x
    git init -q --bare --shared=0664 "$repo_path"
    find "$repo_path" -type d -exec chown $USER:share \{\} \;
    find "$repo_path" -type d -exec chmod 0775 \{\} \;
    find "$repo_path" -type f -exec chmod 0664 \{\} \;
    touch "$repo_path/PUBLIC"
    set +x
}

do_make_shared() {
    local repo_path=$1
    test -f "$repo_path/HEAD" || { echo "$repo_path is not a git repository"; exit 1; }
    set -x
    git init -q --bare --shared=0660 "$repo_path"
    find "$repo_path" -type d -exec chown $USER:share \{\} \;
    find "$repo_path" -type d -exec chmod 0770 \{\} \;
    find "$repo_path" -type f -exec chmod 0660 \{\} \;
    chgrp -R share "$repo_path"
    rm -f "$repo_path/PUBLIC"
    set +x
}

do_make_private() {
    local repo_path=$1
    test -f "$repo_path/HEAD" || { echo "$repo_path is not a git repository"; exit 1; }
    set -x
    git init -q --bare --shared=0600 "$repo_path"
    find "$repo_path" -type d -exec chown $USER:$USER \{\} \;
    find "$repo_path" -type d -exec chmod 0700 \{\} \;
    find "$repo_path" -type f -exec chmod 0600 \{\} \;
    chgrp -R $USER "$repo_path"
    rm -f "$repo_path/PUBLIC"
    set +x
}

case "$1" in
    list) do_list "$2" ;;
    create-public) do_create_public ;;
    create-shared) do_create_shared ;;
    create-private) do_create_private ;;
    make-public) do_make_public "$2" ;;
    make-shared) do_make_shared "$2" ;;
    make-private) do_make_private "$2" ;;
    show)
        shift
        do_show $*
        ;;
    mirror) do_mirror "$2" ;;
    *)
        cat <<HERE
Unknown subcommand: '$1'

Subcommands of git repo:
  list [<dir>]        List infos (optional: only below <dir>)
  show <dir...>       Show Commands for repo <dir>
  mirror <dir>        Mirror git repositories under <dir> by looking
                      for 'remote.origin.mirror = true' config flag.
		      Also set: 'fetch = +refs/*:refs/*'
  create-public       Create new repository that is
                        - Read/Write by Owner
                        - Read/Write by 'share' group.
                        - Read-Only through www via cgit
  create-shared       Create new repository that is
                        - Read/Write by Owner
                        - Read/Write by 'share' group.
  create-private      Create new repository that is
                        - Read/Write by Owner only
  make-public <dir>   Change permissions to public
  make-shared <dir>   Change permissions to shared
  make-private <dir>  Change permissions to private


  ☛ Base directory of repositories: $BASEDIR
  ☛ Color codes: ${C_bg_magenta}public${C_bg_default}, ${C_bg_lblue}shared${C_bg_default}, ${C_bg_green}private${C_bg_default}
  ☛ For limited ssh-access with current user configure ~/.ssh/authorized_keys with:
      command=\"git shell -c \\\"\$SSH_ORIGINAL_COMMAND\\\"\",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-rsa AAAAB3.....
HERE
        ;;
esac