File: git-utils.sh

package info (click to toggle)
cockpit-podman 67-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 5,520 kB
  • sloc: javascript: 13,262; python: 6,107; sh: 579; makefile: 146; xml: 16
file content (139 lines) | stat: -rw-r--r-- 4,755 bytes parent folder | download | duplicates (2)
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
# doesn't do anything on its own.  must be sourced.

# The script which sources this script must set the following variables:
#   GITHUB_REPO = the relative repo name of the submodule on github
#   SUBDIR = the location in the working tree where the submodule goes
# We also expect `set -eu`.
[ -n "${GITHUB_REPO}" ]
[ -n "${SUBDIR}" ]

# Set by git-rebase for spawned actions
unset GIT_DIR GIT_EXEC_PATH GIT_PREFIX GIT_REFLOG_ACTION GIT_WORK_TREE

GITHUB_BASE="${GITHUB_BASE:-cockpit-project/cockpit}"
GITHUB_REPOSITORY="${GITHUB_BASE%/*}/${GITHUB_REPO}"
HTTPS_REMOTE="https://github.com/${GITHUB_REPOSITORY}"
SSH_REMOTE="git@github.com:${GITHUB_REPOSITORY}"

CACHE_DIR="${XDG_CACHE_HOME-${HOME}/.cache}/cockpit-dev/${GITHUB_REPOSITORY}.git"

if [ "${V-}" = 0 ]; then
    message() { printf "  %-8s %s\n" "$1" "$2" >&2; }
    quiet='--quiet'
else
    message() { :; }
    quiet=''
fi

init_cache() {
    if [ ! -d "${CACHE_DIR}" ]; then
        message INIT "${CACHE_DIR}"
        mkdir -p "${CACHE_DIR}"
        git init --bare --template='' ${quiet} "${CACHE_DIR}"
        git --git-dir "${CACHE_DIR}" remote add origin "${HTTPS_REMOTE}"
    fi
}

# runs a git command on the cache dir
git_cache() {
    init_cache
    git --git-dir "${CACHE_DIR}" "$@"
}

# reads the named gitlink from the current state of the index
# returns (ie: prints) a 40-character commit ID
get_index_gitlink() {
    if ! git ls-files -s "$1" | grep -E -o '\<[[:xdigit:]]{40}\>'; then
        echo "*** couldn't read gitlink for file $1 from the index" >&2
        exit 1
    fi
}

# This checks if the given argument "$1" (already) exists in the repository
# we use git rev-list --objects to to avoid problems with incomplete fetches:
# we want to make sure the complete commit is there
check_ref() {
    git_cache rev-list --quiet --objects "$1" -- 2>/dev/null
}

# Fetch a specific commit ID into the cache
# Either we have this commit available locally (in which case this function
# does nothing), or we need to fetch it.  There's no chance that the object
# changed on the server, because we define it by its checksum.
fetch_sha_to_cache() {
    sha="$1"

    # No "offline mode" here: we either have the commit, or we don't
    if ! check_ref "${sha}"; then
        message FETCH "${SUBDIR}  [ref: ${sha}]"
        git_cache fetch --no-tags ${quiet} origin "${sha}"
        # tag it to keep it from being GC'd.
        git_cache tag "sha-${sha}" "${sha}"
    fi
}

# General purpose "fetch" function to be used with tags, refs, or nothing at
# all (to fetch everything).  This checks the server for updates, because all
# of those things might change at any given time.  Supports an "offline" mode
# to skip the fetch and use the possibly-stale local version, if we have it.
fetch_to_cache() {
    # We're fetching a named ref (or all refs), which means:
    #  - we should always do the fetch because it might have changed. but
    #  - we might be able to skip updating in case we already have it
    if [ -z "${OFFLINE-}" ]; then
        message FETCH "${SUBDIR}  ${1+[ref: $*]}"
        git_cache fetch --prune ${quiet} origin "$@"
    fi
}

# Get the content of "$2" from cache commit "$1"
cat_from_cache() {
    git_cache cat-file blob "$1:$2"
}

# Consistency checking: for a given cache commit "$1", check if it contains a
# file "$2" which is equal to the file "$3" present in the working tree.
cmp_from_cache() {
    cat_from_cache "$1" "$2" | cmp "$3"
}

# Like `git clone` except that it uses the original origin url and supports
# checking out commit IDs as detached heads.  The target directory must either
# be empty, or not exist.
clone_from_cache() {
    message CLONE "${SUBDIR}  [ref: $1]"
    [ ! -e "${SUBDIR}" ] || rmdir "${SUBDIR}"
    mkdir "${SUBDIR}"
    cp -a --reflink=auto "${CACHE_DIR}" "${SUBDIR}/.git"
    git --git-dir "${SUBDIR}/.git" config --unset core.bare
    git -c advice.detachedHead=false -C "${SUBDIR}" checkout ${quiet} "$1"
}

# This stores a .tar file from stdin into the cache as a tree object.
# Returns the ID.  Opposite of `git archive`, basically.
tar_to_cache() {
    # Need to do this before we set the GIT_* variables
    init_cache

    # Use a sub-shell to enable cleanup of the temporary directory
    (
        tmpdir="$(mktemp --tmpdir --directory cockpit-tar-to-git.XXXXXX)"
        trap "rm -r '${tmpdir}'" EXIT

        export GIT_INDEX_FILE="${tmpdir}/tmp-index"
        export GIT_WORK_TREE="${tmpdir}/work"

        mkdir "${GIT_WORK_TREE}"
        cd "${GIT_WORK_TREE}"

        tar --extract --exclude '.git*'
        message INDEX "${SUBDIR}"
        git_cache add --all
        git_cache write-tree
    )
}

 # Small helper to run a git command on the cache directory
cmd_git() {
    git_cache "$@"
}