summaryrefslogtreecommitdiff
path: root/git-repo
blob: 5917e5c1d4392da1936e98b78376573a246caf90 (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
#!/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"
			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