summaryrefslogtreecommitdiff
path: root/bin/common/gt
blob: 7609e9006c1c5476b6122b20d095d22f13de16f3 (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
#!/bin/sh

# Git Trach, track the state of multiple repos from a single file.

# dependencies:
# - git
# - $EDITOR: -e

repos=$HOME/sync/share/git-track.txt
# prevent file not found errors
touch "$repos" || exit 1

help() {
	cat >&2 <<EOF
usage: gt [OPTION]
-a PATH         add repo 
-s              update and show status of each repo
-c COMMAND      run 'git COMMAND' in each repo
-h              show this help
-l              list repos
-e 		edit repos in \$EDITOR
-f FILE         use FILE as list of repos
EOF
}

# fetch repository prettily, outputs nothing if failed
fetch() {
	# fetch with one-line printing of progress
	git fetch --progress 2>/dev/null | while read -r line; do # \r\033[0K : clear current line
		printf >&2 '\r\033[0K%s' "$line"
	done
}

# Print repositories prettily
# This function function prints animations (eg. clearing the line)
# to stderr and the final status line is outputted to stdout.
status() {
	while read -r repo; do
		repo_pretty="$(printf '%s' "$repo" | sed "s@$HOME@~@")"

		if [ ! -d "$repo" ]; then
			printf '%s missing\n' "$repo_pretty"
			continue
		fi

		# absolute path
		cd "$repo"

		# replace line with status
		printf >&2 '\r\033[0K'

		status="$(git status --porcelain 2>/dev/null |
			awk '{print $1}' |
			sort | uniq | tr -s '?' |
			tr -d '\n')"
		remote="$(git branch -v 2>/dev/null |
			sed '/^*/!d;s/ahead/↑/;s/behind/↓/;s/[^↓↑]*//g')"

		printf '%s %s %s\n' "$repo_pretty" "$status" "$remote"
	done <"$repos"
}

# run git command in each repo
# $1: command
repos_cmd() {
	while read -r repo; do
		if [ ! -d "$repo" ]; then
			printf '%s missing\n' "$repo_pretty"
			continue
		fi

		repo_pretty="$(printf '%s' "$repo" | sed "s@$HOME@~@")"
		printf ''\''%s'\'' in %s' "$1" "$repo_pretty"
		(
			cd "$repo"
			git "$1" >/dev/null 2>&1
			[ $? -gt 0 ] && s="x" || s="o"
			printf '\r\033[0K%s: %s\n' "$repo_pretty" "$s"
		)
	done <"$repos"
}

# no options
if [ -z "$1" ]; then
	help
	exit 1
fi

while getopts ":a:c:f:lshe" opt; do
	case "$opt" in
	a)
		cd "$OPTARG" || exit 1
		r="$(git rev-parse --show-toplevel)"
		[ "$r" ] || exit 2

		if grep "$r" "$repos" >/dev/null 2>&1; then
			printf >&2 'added already.\n'
			exit 2
		fi

		printf '%s\n' "$r" >>"$repos"

		printf >&2 'added.\n'
		;;
	c)
		f_command=1
		f_arg="$OPTARG"
		;;
	s) f_status=1 ;;
	l) cat "$repos" ;;
	e) $EDITOR "$repos" ;;
	f) repos="$OPTARG" ;;
	h) help ;;
	:)
		printf >&2 -- '-%s requires argument\n' "$OPTARG"
		exit 1
		;;
	?)
		printf >&2 -- 'Invalid option: -%s\n' "$OPTARG"
		exit 1
		;;
	esac
done

# commands hereafter must happen in order

[ "$(wc -l <"$repos")" -gt 0 ] || exit 0

if [ "$f_command" ]; then
	repos_cmd "$f_arg"
fi

if [ "$f_status" ]; then
	status
fi

# eval "herbe $(status | sed 's/"/\"/g;s/.*/"&"/' | tr '\n' ' ')"