odfstudio: add upstream maintenance tools
Change-Id: I352b0b0bf55ba845afd6289250060b7af44a26ee
This commit is contained in:
Executable
+251
@@ -0,0 +1,251 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -u
|
||||
|
||||
die() {
|
||||
echo "ERROR: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
section() {
|
||||
printf '\n=== %s ===\n' "$1"
|
||||
}
|
||||
|
||||
val() {
|
||||
[ -n "${1:-}" ] && printf '%s' "$1" || printf '%s' "-"
|
||||
}
|
||||
|
||||
git rev-parse --is-inside-work-tree >/dev/null 2>&1 \
|
||||
|| die "Not inside a Git repository"
|
||||
|
||||
ROOT=$(git rev-parse --show-toplevel)
|
||||
BRANCH=$(git symbolic-ref --quiet --short HEAD 2>/dev/null || echo "DETACHED")
|
||||
HEAD_COMMIT=$(git rev-parse --short=12 HEAD)
|
||||
|
||||
cd "$ROOT" || exit 1
|
||||
|
||||
# ------------------------------------------------------------
|
||||
section "REPOSITORY"
|
||||
|
||||
echo "path : $ROOT"
|
||||
echo "branch : $BRANCH"
|
||||
echo "HEAD : $HEAD_COMMIT"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
section "IDENTITY"
|
||||
|
||||
GLOBAL_NAME=$(git config --global --get user.name 2>/dev/null || true)
|
||||
GLOBAL_EMAIL=$(git config --global --get user.email 2>/dev/null || true)
|
||||
|
||||
LOCAL_NAME=$(git config --local --get user.name 2>/dev/null || true)
|
||||
LOCAL_EMAIL=$(git config --local --get user.email 2>/dev/null || true)
|
||||
|
||||
EFFECTIVE_NAME=$(git config --get user.name 2>/dev/null || true)
|
||||
EFFECTIVE_EMAIL=$(git config --get user.email 2>/dev/null || true)
|
||||
|
||||
printf 'global : %s <%s>\n' "$(val "$GLOBAL_NAME")" "$(val "$GLOBAL_EMAIL")"
|
||||
printf 'local : %s <%s>\n' "$(val "$LOCAL_NAME")" "$(val "$LOCAL_EMAIL")"
|
||||
printf 'effective : %s <%s>\n' "$(val "$EFFECTIVE_NAME")" "$(val "$EFFECTIVE_EMAIL")"
|
||||
|
||||
# ------------------------------------------------------------
|
||||
section "REMOTES"
|
||||
|
||||
for remote in $(git remote); do
|
||||
FETCH_URL=$(git remote get-url "$remote" 2>/dev/null || true)
|
||||
PUSH_URL=$(git remote get-url --push "$remote" 2>/dev/null || true)
|
||||
|
||||
echo
|
||||
echo "[$remote]"
|
||||
echo " fetch : $(val "$FETCH_URL")"
|
||||
echo " push : $(val "$PUSH_URL")"
|
||||
done
|
||||
|
||||
# ------------------------------------------------------------
|
||||
section "SUBMODULES"
|
||||
|
||||
if [ ! -f .gitmodules ]; then
|
||||
echo "(none)"
|
||||
else
|
||||
git config -f .gitmodules \
|
||||
--get-regexp '^submodule\..*\.path$' 2>/dev/null |
|
||||
while read -r key path; do
|
||||
|
||||
name=${key#submodule.}
|
||||
name=${name%.path}
|
||||
|
||||
declared=$(
|
||||
git config -f .gitmodules \
|
||||
--get "submodule.$name.url" 2>/dev/null || true
|
||||
)
|
||||
|
||||
resolved=$(
|
||||
git config \
|
||||
--get "submodule.$name.url" 2>/dev/null || true
|
||||
)
|
||||
|
||||
commit=$(
|
||||
git ls-tree HEAD "$path" 2>/dev/null |
|
||||
awk '{print $3}'
|
||||
)
|
||||
|
||||
echo
|
||||
echo "[$name]"
|
||||
echo " path : $path"
|
||||
echo " declared : $(val "$declared")"
|
||||
echo " resolved : $(val "$resolved")"
|
||||
echo " commit : $(val "$commit")"
|
||||
done
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------
|
||||
section "SSH ALIASES"
|
||||
|
||||
SSH_CONFIG="$HOME/.ssh/config"
|
||||
|
||||
if [ ! -f "$SSH_CONFIG" ]; then
|
||||
echo "(no ~/.ssh/config)"
|
||||
else
|
||||
awk '
|
||||
/^Host[ \t]+/ {
|
||||
host=$0
|
||||
sub(/^[ \t]*Host[ \t]+/, "", host)
|
||||
print ""
|
||||
print "[" host "]"
|
||||
next
|
||||
}
|
||||
|
||||
/^[ \t]*(HostName|User|Port|IdentityFile|IdentitiesOnly)[ \t]+/ {
|
||||
key=$1
|
||||
value=$2
|
||||
printf " %-15s %s\n", key ":", value
|
||||
}
|
||||
' "$SSH_CONFIG"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------
|
||||
section "SSH BY REMOTE"
|
||||
|
||||
for remote in $(git remote); do
|
||||
|
||||
url=$(git remote get-url "$remote" 2>/dev/null || true)
|
||||
|
||||
case "$url" in
|
||||
ssh://*)
|
||||
;;
|
||||
*@*:*)
|
||||
;;
|
||||
*)
|
||||
echo
|
||||
echo "[$remote]"
|
||||
echo " protocol : non-SSH"
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
# Extract SSH destination from URL.
|
||||
if [[ "$url" =~ ^ssh://([^/@]+@)?([^/:]+)(:([0-9]+))?/.*$ ]]; then
|
||||
url_user=${BASH_REMATCH[1]%@}
|
||||
url_host=${BASH_REMATCH[2]}
|
||||
url_port=${BASH_REMATCH[4]}
|
||||
elif [[ "$url" =~ ^([^@]+)@([^:]+):.*$ ]]; then
|
||||
url_user=${BASH_REMATCH[1]}
|
||||
url_host=${BASH_REMATCH[2]}
|
||||
url_port=""
|
||||
else
|
||||
echo
|
||||
echo "[$remote]"
|
||||
echo " unable to parse: $url"
|
||||
continue
|
||||
fi
|
||||
|
||||
SSH_OUTPUT=$(ssh -G "$url_host" 2>/dev/null || true)
|
||||
|
||||
cfg_user=$(printf '%s\n' "$SSH_OUTPUT" |
|
||||
awk '$1=="user" {print $2; exit}')
|
||||
|
||||
cfg_host=$(printf '%s\n' "$SSH_OUTPUT" |
|
||||
awk '$1=="hostname" {print $2; exit}')
|
||||
|
||||
cfg_port=$(printf '%s\n' "$SSH_OUTPUT" |
|
||||
awk '$1=="port" {print $2; exit}')
|
||||
|
||||
cfg_keys=$(printf '%s\n' "$SSH_OUTPUT" |
|
||||
awk '$1=="identityfile" {print $2}')
|
||||
|
||||
effective_user=${url_user:-$cfg_user}
|
||||
effective_port=${url_port:-$cfg_port}
|
||||
|
||||
echo
|
||||
echo "[$remote]"
|
||||
echo " URL : $url"
|
||||
echo " URL user : $(val "$url_user")"
|
||||
echo " URL host : $(val "$url_host")"
|
||||
echo " URL port : $(val "$url_port")"
|
||||
echo
|
||||
echo " SSH cfg user : $(val "$cfg_user")"
|
||||
echo " SSH cfg host : $(val "$cfg_host")"
|
||||
echo " SSH cfg port : $(val "$cfg_port")"
|
||||
|
||||
while read -r key; do
|
||||
[ -n "$key" ] && echo " identity : $key"
|
||||
done <<< "$cfg_keys"
|
||||
|
||||
echo
|
||||
echo " effective user: $(val "$effective_user")"
|
||||
echo " effective host: $(val "$cfg_host")"
|
||||
echo " effective port: $(val "$effective_port")"
|
||||
|
||||
if [ -n "$url_user" ] &&
|
||||
[ -n "$cfg_user" ] &&
|
||||
[ "$url_user" != "$cfg_user" ]; then
|
||||
echo " NOTICE : URL user overrides SSH config user"
|
||||
fi
|
||||
|
||||
if [ -n "$url_port" ] &&
|
||||
[ -n "$cfg_port" ] &&
|
||||
[ "$url_port" != "$cfg_port" ]; then
|
||||
echo " NOTICE : URL port overrides SSH config port"
|
||||
fi
|
||||
done
|
||||
|
||||
# ------------------------------------------------------------
|
||||
section "SSH AGENT"
|
||||
|
||||
if ssh-add -l >/dev/null 2>&1; then
|
||||
ssh-add -l
|
||||
else
|
||||
echo "INFO: no SSH agent available or no key loaded"
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------
|
||||
section "OBSERVATIONS"
|
||||
|
||||
OBS=0
|
||||
|
||||
if [ -n "$LOCAL_NAME$LOCAL_EMAIL" ] &&
|
||||
{ [ "$LOCAL_NAME" != "$GLOBAL_NAME" ] ||
|
||||
[ "$LOCAL_EMAIL" != "$GLOBAL_EMAIL" ]; }; then
|
||||
|
||||
echo "NOTICE: local Git identity overrides global identity"
|
||||
OBS=$((OBS + 1))
|
||||
fi
|
||||
|
||||
if [ -f .gitmodules ]; then
|
||||
while read -r key url; do
|
||||
case "$url" in
|
||||
../*|./*)
|
||||
echo "INFO: relative submodule URL: $key -> $url"
|
||||
OBS=$((OBS + 1))
|
||||
;;
|
||||
esac
|
||||
done < <(
|
||||
git config -f .gitmodules \
|
||||
--get-regexp '^submodule\..*\.url$' 2>/dev/null
|
||||
)
|
||||
fi
|
||||
|
||||
if [ "$OBS" -eq 0 ]; then
|
||||
echo "No notable configuration difference detected."
|
||||
fi
|
||||
|
||||
echo
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ODF_BRANCH="${1:-main}"
|
||||
UPSTREAM_BRANCH="${2:-upstream/master}"
|
||||
|
||||
git rev-parse --git-dir >/dev/null 2>&1 || {
|
||||
echo "ERROR: not inside a Git repository" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
git rev-parse --verify "$ODF_BRANCH" >/dev/null
|
||||
git rev-parse --verify "$UPSTREAM_BRANCH" >/dev/null
|
||||
|
||||
BASE="$(git merge-base "$ODF_BRANCH" "$UPSTREAM_BRANCH")"
|
||||
ODF_HEAD="$(git rev-parse "$ODF_BRANCH")"
|
||||
UPSTREAM_HEAD="$(git rev-parse "$UPSTREAM_BRANCH")"
|
||||
|
||||
TMPDIR_WORK="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMPDIR_WORK"' EXIT
|
||||
|
||||
ODF_FILES="$TMPDIR_WORK/odfstudio-files.txt"
|
||||
UPSTREAM_FILES="$TMPDIR_WORK/upstream-files.txt"
|
||||
SHARED_FILES="$TMPDIR_WORK/shared-files.txt"
|
||||
|
||||
git diff --name-only "$BASE".."$ODF_BRANCH" | sort -u > "$ODF_FILES"
|
||||
git diff --name-only "$BASE".."$UPSTREAM_BRANCH" | sort -u > "$UPSTREAM_FILES"
|
||||
|
||||
comm -12 "$ODF_FILES" "$UPSTREAM_FILES" > "$SHARED_FILES"
|
||||
|
||||
ODF_COMMITS="$(git rev-list --count "$UPSTREAM_BRANCH".."$ODF_BRANCH")"
|
||||
UPSTREAM_COMMITS="$(git rev-list --count "$ODF_BRANCH".."$UPSTREAM_BRANCH")"
|
||||
|
||||
ODF_FILE_COUNT="$(wc -l < "$ODF_FILES")"
|
||||
UPSTREAM_FILE_COUNT="$(wc -l < "$UPSTREAM_FILES")"
|
||||
SHARED_FILE_COUNT="$(wc -l < "$SHARED_FILES")"
|
||||
|
||||
echo "ODFStudio upstream synchronization report"
|
||||
echo "========================================="
|
||||
echo
|
||||
printf "BASE %s\n" "$BASE"
|
||||
printf "ODFStudio %s (%s)\n" "$ODF_HEAD" "$ODF_BRANCH"
|
||||
printf "Upstream %s (%s)\n" "$UPSTREAM_HEAD" "$UPSTREAM_BRANCH"
|
||||
|
||||
echo
|
||||
echo "Commits"
|
||||
printf " ODFStudio %8d\n" "$ODF_COMMITS"
|
||||
printf " Upstream %8d\n" "$UPSTREAM_COMMITS"
|
||||
|
||||
echo
|
||||
echo "Modified files since common base"
|
||||
printf " ODFStudio %8d\n" "$ODF_FILE_COUNT"
|
||||
printf " Upstream %8d\n" "$UPSTREAM_FILE_COUNT"
|
||||
printf " Shared %8d\n" "$SHARED_FILE_COUNT"
|
||||
|
||||
echo
|
||||
echo "Upstream activity on shared files"
|
||||
echo "---------------------------------"
|
||||
|
||||
if [[ ! -s "$SHARED_FILES" ]]; then
|
||||
echo "No shared modified files."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
while IFS= read -r file; do
|
||||
count="$(git rev-list --count "$BASE".."$UPSTREAM_BRANCH" -- "$file")"
|
||||
printf "%5d %s\n" "$count" "$file"
|
||||
done < "$SHARED_FILES" | sort -nr
|
||||
Executable
+67
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ODF_BRANCH="${1:-main}"
|
||||
UPSTREAM_BRANCH="${2:-upstream/master}"
|
||||
|
||||
git rev-parse --git-dir >/dev/null 2>&1 || {
|
||||
echo "ERROR: not inside a Git repository" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "ODFStudio upstream sync status"
|
||||
echo "=============================="
|
||||
echo
|
||||
|
||||
printf "Repository : %s\n" "$(git rev-parse --show-toplevel)"
|
||||
printf "Branch : %s\n" "$(git branch --show-current)"
|
||||
printf "HEAD : %s\n" "$(git rev-parse --short=12 HEAD)"
|
||||
printf "ODFStudio : %s\n" "$(git rev-parse --short=12 "$ODF_BRANCH")"
|
||||
printf "Upstream : %s\n" "$(git rev-parse --short=12 "$UPSTREAM_BRANCH")"
|
||||
printf "Base : %s\n" "$(git merge-base "$ODF_BRANCH" "$UPSTREAM_BRANCH" | cut -c1-12)"
|
||||
|
||||
echo
|
||||
echo "Working tree"
|
||||
echo "------------"
|
||||
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
echo "Tracked files : clean"
|
||||
else
|
||||
echo "Tracked files : MODIFIED"
|
||||
git status --short
|
||||
fi
|
||||
|
||||
UNTRACKED="$(git ls-files --others --exclude-standard)"
|
||||
|
||||
if [[ -z "$UNTRACKED" ]]; then
|
||||
echo "Untracked : none"
|
||||
else
|
||||
echo "Untracked :"
|
||||
printf '%s\n' "$UNTRACKED" | sed 's/^/ /'
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Remotes"
|
||||
echo "-------"
|
||||
git remote -v | awk '$3 == "(fetch)" {print}'
|
||||
|
||||
echo
|
||||
echo "Divergence"
|
||||
echo "----------"
|
||||
|
||||
printf "ODFStudio-only commits : "
|
||||
git rev-list --count "$UPSTREAM_BRANCH".."$ODF_BRANCH"
|
||||
|
||||
printf "Upstream-only commits : "
|
||||
git rev-list --count "$ODF_BRANCH".."$UPSTREAM_BRANCH"
|
||||
|
||||
echo
|
||||
echo "Sync readiness"
|
||||
echo "--------------"
|
||||
|
||||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||
echo "NOT READY: tracked working tree is modified."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "READY: tracked working tree is clean."
|
||||
Reference in New Issue
Block a user