summaryrefslogtreecommitdiffstats
path: root/gitmirror-merge.sh
blob: d346602e63a4b40a6e5c7a1eabc42f86ba874415 (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
#!/usr/bin/env bash
set -Eeuo pipefail

MIRROR_REMOTE="gitmirror_remote"
CLONE_URL_PATTERN="file://${PWD}/%s"

msg() {
    echo "==> $1"
}

msg2() {
    echo "--> $1"
}

deepened_fetch() {
    local fetch_spec="$1"
    local exclude="$2"
    if git fetch "${MIRROR_REMOTE}" "${fetch_spec}" \
        --shallow-exclude="${exclude}"; then
        msg2 "Deepen fetch by 1"
        git fetch "${MIRROR_REMOTE}" "${fetch_spec}" --deepen=1
    else
        msg2 "Shallow request possibly empty, retry with depth=1"
        git fetch "${MIRROR_REMOTE}" "${fetch_spec}" --depth=1
    fi
}

merge_patches() {
    if (( $# != 3 )); then
        echo "Usage: $0 REPOSITORY REMOTE BRANCH" >&2
        exit 1
    fi

    local repository="$1"
    local remote="$2"
    local branch="$3"

    if [[ "${remote}" == "${MIRROR_REMOTE}" ]]; then
       msg "REMOTE must not be ${MIRROR_REMOTE}" >&2
       exit 1
    fi

    local copy_name="${repository}-patch-${remote}-${branch}"
    msg "Create working copy ${copy_name}"
    rm -rf "${copy_name}"
    mkdir "${copy_name}"
    pushd "${copy_name}" > /dev/null
    git init
    local clone_url
    # shellcheck disable=2059
    clone_url="$(printf "${CLONE_URL_PATTERN}" "${repository}")"
    git remote add "${MIRROR_REMOTE}" "${clone_url}"

    local patch_branch="patch-for/${branch}"
    msg "Fetch patch at ${patch_branch}"
    local upstream_branch="${remote}/${branch}"
    local remote_upstream_ref="refs/remotes/${upstream_branch}"
    deepened_fetch "${patch_branch}" "${remote_upstream_ref}"

    msg "Fetch upstream at ${upstream_branch}"
    local local_upstream_ref="refs/remotes/${MIRROR_REMOTE}/${upstream_branch}"
    deepened_fetch "${remote_upstream_ref}:${local_upstream_ref}" \
        "refs/heads/${patch_branch}"

    msg "Merge ${patch_branch} into ${upstream_branch}"
    git checkout -b "${branch}" --no-track "${local_upstream_ref}"
    git merge "${MIRROR_REMOTE}/${patch_branch}" --no-ff \
        -m "Merge branch '${patch_branch}' into ${upstream_branch}"

    msg "Push ${branch} to ${MIRROR_REMOTE}"
    git push --force "${MIRROR_REMOTE}" "${branch}:${branch}"

    popd > /dev/null
    rm -rf "${copy_name}"
}

if [[ "$0" == "${BASH_SOURCE[0]}" ]]; then
    merge_patches "$@"
fi