summaryrefslogtreecommitdiffstats
path: root/.local/bin/gitrect-update
blob: dcb1c845b121cc7caf97f4df8a588a80d49f2eca (plain) (blame)
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
#!/usr/bin/env sh

# POSIX shell implementation of Gitrect
#
# Operate on a tempfile and generate a listing of:
#
# prefix-trim-directory,remote=URL,...
#
# For all directories found in $WORKDIR

# Operate on tempfile
TEMPFILE="/tmp/gitrect.temp.$(od -t x1 -An -N6 /dev/urandom |tr -d '\n ')"
touch "$TEMPFILE"

# Reset all variables that might be set
DEFAULT_STATE="$HOME/.local/share/git/repolist"
WORKDIR="$HOME/.code"
verbose=0 # Variables to be evaluated as shell arithmetic should be initialized to a default or validated beforehand.
noconfirm=0

if [ ! "$(command -v git)" ]; then
        echo "git command not found. git must be installed and available in \$PATH to continue"
        exit 1
fi

usage="$(basename "$0") [-h] [-v -w WORKDIR -f FILE]

where:
    -w  set the working directory (default: ${WORKDIR})
    -f  set the git list file (default: ${DEFAULT_STATE})
    -y  yes to all prompts (no confirmation)

    -h  show this help text
    -v  verbose output (multiple flags increase verbosity)
        (ex: '-v -v')
"

while :; do
        case $1 in
        -h | -\? | --help) # Call a "show_help" function to display a synopsis, then exit.
                echo "$usage"
                exit
                ;;
        -f) # Takes an option argument, ensuring it has been specified.
                if [ -n "$2" ]; then
                        DEFAULT_STATE=$2
                        shift
                fi
                ;;
        -f=?*)
                DEFAULT_STATE=${1#*=} # Delete everything up to "=" and assign the remainder.
                ;;
        -f=) # Handle the case of an empty --file=
                printf 'ERROR: "-f" requires a non-empty option argument.\n' >&2
                exit 1
                ;;
        -w) # Takes an option argument, ensuring it has been specified.
                if [ -n "$2" ]; then
                        WORKDIR=$2
                        shift
                fi
                ;;
        -w=?*)
                WORKDIR=${1#*=} # Delete everything up to "=" and assign the remainder.
                ;;
        -w=) # Handle the case of an empty --file=
                printf 'ERROR: "-w" requires a non-empty option argument.\n' >&2
                exit 1
                ;;
        -y)
                noconfirm=1
                ;;
        -v)
                verbose=$((verbose + 1)) # Each -v argument adds 1 to verbosity.
                ;;
        --) # End of all options.
                shift
                break
                ;;
        -?*)
                printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
                ;;
        *) # Default case: If no more options then break out of the loop.
                break ;;
        esac
        shift
done

# Make sure local state folder is available
if [ ! -d "$HOME/.local/share" ]; then
        if [ $verbose -gt 0 ]; then
                echo "Creating directory: $HOME/.local/share"
        fi
        mkdir -p "$HOME"/.local/share
fi

# Rest of the program here.
# If there are input files (for example) that follow the options, they
# will remain in the "$@" positional parameters.

LIST=$(eval "find ${WORKDIR} -type d -name .git")

# for each directory, create a line in TEMPFILE equivalent to:
# short/path/here,origin=url1,upstream=url2,...
#
for dirpath in $LIST; do
        # shortpath trims the working directory prefix and the '.git' suffix from the directory
        shortpath=$(eval "echo $dirpath | sed -e 's,${WORKDIR}/,,' -e 's/\/\.git//'")
        if [ $verbose -gt 1 ]; then
                echo "Repository found: ${shortpath}" >&2
        fi

        cd "${dirpath}/.." || continue       # change into the parent of the git directory
        rems=$(eval "git remote") # list all remotes
        len_rems=${#rems}
        if [ "$len_rems" -lt 1 ]; then
                # If the list of remotes is zero, skip this folder
                continue
        fi

        # We haven't skipped the path, so write it to the file
        printf "%s" "$shortpath" >>"$TEMPFILE"
        for r in $rems; do
                url=$(eval "git remote get-url ${r}")
                printf ",%s=%s" "$r" "$url" >>"$TEMPFILE"
                if [ $verbose -gt 1 ]; then
                        echo "remote: ${r} ${url} " >&2
                fi
        done
        printf "\n" >>"$TEMPFILE"
done

# Sort results
sort -o "${TEMPFILE}" "${TEMPFILE}"

diffval=$(eval "diff -b ${DEFAULT_STATE} ${TEMPFILE}")
if [ $? -eq 0 ]; then
        if [ $verbose -gt 0 ]; then
                echo "No change. Configuration already up to date." >&2
        fi
        # Cleanup temp file
        rm "${TEMPFILE}"
else
        if [ $verbose -gt 0 ]; then
                echo "Diff between current and updated state: " >&2
                echo "$diffval" >&2
        fi
        if [ $noconfirm -ne 0 ]; then
                # Overwite origiaal with the new
                mv "${TEMPFILE}" "${DEFAULT_STATE}"
        else
                printf 'Accept changes (y/n)? \n'
                old_stty_cfg=$(stty -g)
                stty raw -echo
                answer=$(while ! head -c 1 | grep -i '[ny]'; do true; done)
                stty "$old_stty_cfg"
                if [ "$answer" != "${answer#[Yy]}" ]; then
                        echo "Updating file..."
                        # Overwite origiaal with the new
                        mv "${TEMPFILE}" "${DEFAULT_STATE}"
                else
                        echo "Discarding changes..."
                        rm "${TEMPFILE}"
                fi
        fi
fi