git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Suggetsions for collaboration workflows in large repos

From
Matthew Hughes <matthewhughes934@gmail.com>
Date
May 29, 2026, 18:06 UTC
Message-ID
<ahnUeESE1x802Z9N@desktop>
In-Reply-To
<20260529163117.z2auhbg4sdxxgmis@archP14s>
On Fri, May 29, 2026 at 05:31:17PM +0100, Matthew Hughes wrote:
Show 6 quoted lines
> I thought about doing something like tracking
> `refs/heads*/some-colleague-branch` from the remote, since with the wildcard
> `*` I at least won't the fatal error on the missing reference during fetch, but
> that risks my config containing an ever growing list of such wildcards, or a
> bunch of manual work occasionally cleaning up old ones (or maybe that could be
> automated).
I hacked some scripts to automate this. Firstly, one for fetching:
1. Fetches the branch
2. Adds a fetch config with wildcard hacks so `git fetch` brings in updates for
  that branch (the refspec should match _exactly_ that branch and never
  anything more)
3. Adds a separate ref to record that we're tracking this branch (so something
  knows to clean it up later)
    #!/usr/bin/env bash
    set -o errexit -o pipefail -o nounset
    # save command as e.g. git-fetch-other
    CMD_NAME="$(basename "$0" | sed 's/git-//g')"
    if [ $# -lt 1 ]
    then
        echo "usage: git $CMD_NAME branch-name [ remote-name ]" >&2
        exit 1
    fi
    BRANCH_NAME="$1"
    REMOTE_NAME="${2:-origin}"
    FETCH_CONFIG_NAME="remote.$REMOTE_NAME.fetch"
    git fetch "$REMOTE_NAME" "$BRANCH_NAME"
    git checkout -b "$BRANCH_NAME"
    # we want to record that we are tracking this branch, to do this create
    # a new ref whose name tells us what we're tracking, but whose value is
    # unimportant. So as a placeholder value just use the hash of an empty tree
    # taken from https://git.kernel.org/pub/scm/git/git.git/commit/?id=9c8a294a1ae1335511475db9c0eb8841c0ec9738
    EMPTY_TREE_REF="$(git hash-object -t tree /dev/null)"
    # refspec used to track the branch: we expect branches to be deleted from the
    # upstream when merged so tracking exactly:
    # "+refs/heads/$BRANCH_NAME:refs/remotes/$REMOTE_NAME/$BRANCH_NAME" will error
    # when we go to fetch that exact ref after its removed upstream.
    # so HACK around this: add wildcards that we still expect to only ever match
    # this exact branch (but doesn't have the issue of git complaining when it
    # tries to fetch an _exact_ ref)
    TRACKING_REFSPEC="+refs/heads*/$BRANCH_NAME:refs/remotes*/$REMOTE_NAME/$BRANCH_NAME"
    # record that we're tracking this branch. First check we've not already
    # recorded this, then ...
    if ! git config get --local --fixed-value --value "$TRACKING_REFSPEC" "$FETCH_CONFIG_NAME" >/dev/null
    then
        # ... set the config to track it for fetching, and ...
        git config set --comment "$CMD_NAME: tracking at $(date -I)"  --local --append "$FETCH_CONFIG_NAME" "$TRACKING_REFSPEC"
        # ... record that we have special cased this tracking
        git update-ref "refs/tracked/$REMOTE_NAME/$BRANCH_NAME" "$EMPTY_TREE_REF"
    fi
And the cleanup script (needs to be run periodically):
1. Collects all the remote branches we know about
2. Checks all the references from step 3. above and checks if any branches
defined there are missing remotes (I have fetch.prune=true to keep the remote
tracking references up-to-date)
3. If they are, drops the tracking config for that branch
    #!/usr/bin/env bash
    set -o errexit -o pipefail -o nounset
    REMOTE_NAME="${1:-origin}"
    TRACKED_REF_PREFIX="refs/tracked/$REMOTE_NAME"
    REMOTE_REF_PREFIX="refs/remotes/$REMOTE_NAME"
    declare -A remote_branch_lookup
    while read -r remote_ref
    do
        # strip prefix, e.g. 'refs/remotes/origin/some-branch' -> 'some-branch'
        branch_name="${remote_ref#$REMOTE_REF_PREFIX/}"
        remote_branch_lookup["$branch_name"]=1
    done < <(git for-each-ref --format='%(refname)' "$REMOTE_REF_PREFIX/")
    while read -r tracking_info
    do
        tracked_branch="${tracking_info#$TRACKED_REF_PREFIX/}"
        if ! [[ -v "remote_branch_lookup[$tracked_branch]" ]]
        then
            echo "branch $tracked_branch has been removed from the remote, untracking it"
            git update-ref -d "$TRACKED_REF_PREFIX/$tracked_branch"
            tracking_refspec="+refs/heads*/$tracked_branch:refs/remotes*/$REMOTE_NAME/$tracked_branch"
            git config unset --local --fixed-value --value "$tracking_refspec" "remote.$REMOTE_NAME.fetch"
        fi
    done < <(git for-each-ref --format='%(refname)' "$TRACKED_REF_PREFIX/")

So functionally I think this allows for the workflow I want, but does feel like a big ol' hack :>

Previous: Matthew HughesNext: Toon Claes
Message 4 of 5 in “Suggetsions for collaboration workflows in large repos”
  1. Matthew HughesMay 29, 2026
  2. Ben KnobleMay 29, 2026
  3. Matthew HughesJun 2, 2026
  4. Matthew HughesMay 29, 2026
  5. Toon ClaesJun 3, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.