This is my workflow to handle forced updates in the git repository conversion as smoothly as regular updates:
1. When I start working on a change, say for PR 12345, I create two branches with a short name describing the change: git branch pr12345-mumblefix-base origin/trunk git checkout -b pr12345-mumblefix pr12345-mumblefix-base 2. I commit some changes on the branch with `git commit -i' to interactively select the hunks to commit so I can keep separate functional (or non-functional) changes. Sometimes I want to amend a previous commit, so I find the commit hash, say 1234abcd, and make a fixup commit: git commit --fixup 1234abcd This makes a magic commit message `fixup! <title of 1234abcd>'. Then from time to time I rebase to apply the fixups: git rebase -i --autosquash pr12345-mumblefix-base 3. Sometimes I push changes from the branch to the CVS repository with git cvsexportcommit: git cvsexportcommit -c -p -v -w ~/cvs/src 1234abcd If the change is near an RCS id line ($NetBSD: ...$), it is sometimes necessary to omit `-p' so the patch is allowed to apply with smaller context, but I manually eyeball it before I do that, just in case. 4. When I want to update the repository, I use the attached script to create a new branch pr12345-mumblefix-v2 on a new base pr12345-mumblefix-v2-base with all the same commits: /path/to/git-update.sh pr12345-mumblefix origin/trunk or, next time, to make pr12345-mumblefix-v3: /path/to/git-update.sh pr12345-mumblefix-v2 origin/trunk Of course, there might be merge conflicts to resolve. Sometimes if I've already committed changes git will figure that out; sometimes git won't figure it out but it's just a matter of skipping a commit with `git rebase --skip'. It's not highly polished -- if you want to give up on resolving the merge conflicts, you can do `git rebase --abort', but git-update.sh won't clean up the branches it already created. Nevertheless, it's been very handy for my development needs. (You can also put it in your PATH and then say `git update' instead of `/path/to/git-update.sh'. Not sure if git plans to add a standard `git update' command, though.) 5. When I'm done and I've pushed all the changes to CVS and the branch is now empty on update, so pr12345-mumblefix-vN is the same as pr12345-mumblefix-vN-base, I rename all the branches to be closed/pr12345-mumblefix-vN and closed/pr12345-mumblefix-vN-base. Not perfect but easy to ignore in `git branch' output. (You can also just delete the branches, if the proposition of deleting history doesn't make your skin crawl like it does for me.)
#!/bin/sh # Copyright (c) 2023 Taylor R. Campbell # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN # NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set -Ceu run() { echo '#' "$@" ${DRY_RUN+:} "$@" } oldbranch=$1 newbase=${2-origin/trunk} case $oldbranch in *-v[0-9] | *-v[0-9][0-9]) prefix=${1%-v*} oldversion=${1##*-v} newversion=$((oldversion + 1)) ;; *) prefix=$1 newversion=2 ;; esac newbranch=${prefix}-v${newversion} if git rev-parse --verify --quiet ${newbranch}-base >/dev/null; then printf >&2 'git-update: new base %s already exists\n' ${newbranch}-base exit 1 fi if git rev-parse --verify --quiet ${newbranch} >/dev/null; then printf >&2 'git-update: new branch %s already exists\n' ${newbranch} exit 1 fi if ! run git diff --stat --exit-code; then printf >&2 'git-update: dirty working tree, aborting\n' exit 1 fi if ! run git diff --cached --stat --exit-code; then printf >&2 'git-update: dirty staging area, aborting\n' exit 1 fi run git branch ${newbranch}-base $newbase run git branch $newbranch $oldbranch run git rebase --onto ${newbranch}-base ${oldbranch}-base $newbranch