|
| 1 | +#!/bin/sh |
| 2 | +# A more helpful replacement for 'git submodule update --init'. |
| 3 | +# |
| 4 | +# It leaves the remote 'origin' pointing at the upstream projects, but can |
| 5 | +# use local git-subtrac branches as a data source so that most of the time |
| 6 | +# it doesn't actually need to fetch from the upstream. |
| 7 | +# |
| 8 | +# Use this whenever you want to get your submodules back in sync. |
| 9 | +# |
| 10 | +set -e |
| 11 | +cd "$(dirname "$0")" # move to git worktree root |
| 12 | +topdir="$PWD" # absolute path of git worktree root |
| 13 | + |
| 14 | +# For initial 'git submodule update', the --no-fetch option is ignored, |
| 15 | +# and it tries to talk to the origin repo for no good reason. Let's override |
| 16 | +# the origin repo URL to fool git into not doing that. |
| 17 | +git submodule status | while read -r commit path junk; do |
| 18 | + git submodule init -- "$path" |
| 19 | +done |
| 20 | +git config --local --get-regexp '^submodule\..*\.url$' | while read k v; do |
| 21 | + git config "$k" . |
| 22 | +done |
| 23 | + |
| 24 | +# In each submodule, make sure info/alternates is set up to retrieve |
| 25 | +# objects directly from the parent repo (git-subtrac objects), bypassing |
| 26 | +# the need to fetch anything. If someone has previously checked out a |
| 27 | +# submodule without setting these values, this will fix them up. |
| 28 | +for config in .git/modules/*/config; do |
| 29 | + [ -f "$config" ] || continue |
| 30 | + |
| 31 | + dir=$(dirname "$config") |
| 32 | + echo "$topdir/.git/objects" >"$dir/objects/info/alternates" |
| 33 | +done |
| 34 | + |
| 35 | +# Make sure any remaining submodules have been checked out at least once, |
| 36 | +# referring to the toplevel repo for all objects. |
| 37 | +# |
| 38 | +# TODO(apenwarr): --merge is not always the right option. |
| 39 | +# eg. when checking out old revisions, we'd rather just roll the submodule |
| 40 | +# backwards too. But git submodule doesn't have a good way to do that |
| 41 | +# safely, so after a checkout, you can run git-stash-all.sh by hand to |
| 42 | +# rewind the submodules. |
| 43 | +git submodule update --init --no-fetch --reference="$PWD" --recursive --merge |
| 44 | + |
| 45 | +# Make sure all submodules are *now* (after initial checkout) using the |
| 46 | +# latest URL from .gitmodules for their 'origin' URL. |
| 47 | +git submodule --quiet sync --recursive |
| 48 | + |
| 49 | +git submodule status --cached | while read -r commit path junk; do |
| 50 | + # fix superproject conflicts caused by trying to merge submodules, |
| 51 | + # if any. These happen when two different commits try to change the |
| 52 | + # same submodule in incompatible ways. To resolve it, we'll check out |
| 53 | + # the first one and try to git merge the second. (Why git can't just |
| 54 | + # do this by itself is... one of the many problems with submodules.) |
| 55 | + cid2= |
| 56 | + cid3= |
| 57 | + git ls-files --unmerged -- "$path" | while read -r mode hash rev junk; do |
| 58 | + if [ "$rev" = "2" ]; then |
| 59 | + (cd "$path" && git checkout "$hash" -- || true) |
| 60 | + cid2=$hash |
| 61 | + fi |
| 62 | + if [ "$rev" = "3" ]; then |
| 63 | + cid3=$hash |
| 64 | + (cd "$path" && git merge "$hash" -- || true) |
| 65 | + git add -- "$path" |
| 66 | + fi |
| 67 | + done |
| 68 | + |
| 69 | + commit=${commit#-} |
| 70 | + commit=${commit#+} |
| 71 | + ( |
| 72 | + cd "$path" |
| 73 | + |
| 74 | + main=$(git rev-parse --verify --quiet main || true) |
| 75 | + head=$(git rev-parse --verify HEAD) |
| 76 | + |
| 77 | + if [ -n "$main" ] && |
| 78 | + ! git merge-base main "$commit" >/dev/null; then |
| 79 | + # main and $commit have no common history. |
| 80 | + # It's probably dangerous. Move it aside. |
| 81 | + git branch -f -m main main.probably-broken |
| 82 | + fi |
| 83 | + |
| 84 | + # update --merge can't rewind the branch, only move it |
| 85 | + # forward. Give a warning if we notice this problem. |
| 86 | + if [ "$head" != "$commit" ]; then |
| 87 | + echo "$path:" >&2 |
| 88 | + echo " Couldn't checkout non-destructively." >&2 |
| 89 | + echo " You can try to fix it by hand, or" >&2 |
| 90 | + echo " use git-stash-all.sh if you want to force it." >&2 |
| 91 | + fi |
| 92 | + ) |
| 93 | +done |
0 commit comments