Although amon's answer will likely be relevant for most of everyone else attempting this sort of "creative" usage of git, for those that want to use it like this, I ended up using the following scripts to perform all updates automatically:
Warning: This was made for a hobby project with only 1 collaborator, so the code quality is low. Don't use this for anything large or if you're planning on having multiple people work simultaneously, as it will break everything.
The plan is to have a .branch-deps.json
file in master
that has all of the dependencies of the project, as such:
{
"a": [],
"b": [],
"c": ["b"]
}
Then we have two scripts, one to update a specific branch, and another to update all branches:
update_branch.py
:
#!/bin/env python3
# Imports
import subprocess
import json
# Updates a branch `orig_branch` with deps `branch_deps`
def update_branch(orig_branch, branch_deps):
# Checkout a temp branch on master
subprocess.check_call(["git", "checkout", "-b", "temp", "master", "--quiet"])
# Merge all dependencies
for branch in branch_deps:
print(f"> Merge`{branch}`")
if subprocess.call(["git", "merge", f"{branch}", "--no-ff", "-m", f"Merge branch `{branch}`", "--quiet"]) != 0:
# If we fail to merge, try to `continue` the merge after the user fixes conflicts
while True:
input("Unable to merge, check possible conflicts...")
print("Continuing merge")
if subprocess.call(["git", "merge", "--continue"]) == 0:
break;
# Then rebase the orignal branch
print(f"> Rebase")
subprocess.check_call(["git", "checkout", f"{orig_branch}", "--quiet"])
if subprocess.call(["git", "rebase", "temp", "--quiet"]) != 0:
# If we fail to rebase, try to `continue` the rebase after the user fixes conflicts
while True:
input("Unable to rebase, check possible conflicts...")
print("Continuing rebase")
if subprocess.call(["git", "rebase", "--continue"]) == 0:
break;
# Finally delete the temp branch
subprocess.check_call(["git", "branch", "-d", "temp", "--quiet"])
if __name__ == "__main__":
# If any changes exist, return Err
changes = subprocess.check_output(["git", "status", "--porcelain"]);
if len(changes) != 0:
print("You must stash or commit changes before updating")
exit(1)
# Get the current branch name
orig_branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
orig_branch = orig_branch.decode("utf-8").strip()
if orig_branch == "master":
print("Cannot update master, checkout another branch")
exit(1)
print(f"Updating branch `{orig_branch}`")
# Then read it's dependencies
branch_deps = json.load(open(".branch-deps.json"))[orig_branch]
print(f"Found {len(branch_deps)} dependencies")
# And update it
update_branch(orig_branch, branch)
This script will create a new branch temp
on master
, merge all dependencies, then rebase the original branch on temp
and finally delete temp
.
This would be used when you just want to work on a single branch, but it's dependencies have been updated in the meantime.
update_all_branches.py
#!/bin/env python3
# Imports
import subprocess
import json
import update_branch
# Checksout and updates `branch`, with all branch dependencies in `branch_deps` and the already updated
# branches in `updated_branches`
def checkout_update_branch(branch, branch_deps, updated_branches):
# If we're already updated, return
if branch in updated_branches:
return
# Update any dependencies first
for dep_branch in branch_deps[branch]:
checkout_update_branch(dep_branch, branch_deps, updated_branches)
# Then checkout the branch and update
print(f"`{branch}`:")
subprocess.check_call(["git", "checkout", f"{branch}", "--quiet"])
update_branch.update_branch(branch, branch_deps[branch])
# And set ourselves as updated
updated_branches.add(branch)
if __name__ == "__main__":
# If we have any changes, return Err
changes = subprocess.check_output(["git", "status", "--porcelain"]);
if len(changes) != 0:
print("You must stash or commit changes before updating")
exit(1)
# If we aren't in master, return Err
cur_branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
cur_branch = cur_branch.decode("utf-8").strip()
if cur_branch != "master":
print("Updating all branches must be done from master")
exit(1)
# Else read all branches
branch_deps = json.load(open(".branch-deps.json"))
print(f"Found {len(branch_deps)} branches")
# All updated branches
updated_branches = set()
# Then for each one, update it
for branch in branch_deps:
checkout_update_branch(branch, branch_deps, updated_branches)
# Finally checkout master
subprocess.check_call(["git", "checkout", "master", "--quiet"])
This script will update each branch in .branch-deps.json
. It will also be sure to update dependencies first to ensure every branch ends up updated.
This would be used when master
receives new updates, or a major branch with a ton of dependencies is updated.