Workflow advice for juggling branches?

What are people's workflows around working on multiple things? Do you use multiple clones (or git worktree?)? Do you use branches on the same clone and different build directories for different branches? Something else?

Lately, I've been using 2 clones, and I have two build directories for each clone, one for ReleaseAssert (this re-builds relatively quickly and is more handy if I'm print-debugging) and one for llvm RelWithDebInfoAssert + swift DebugAssert (if I'm using the debugger). However, sometimes switching between branches causes way too much to rebuild.

Earlier, I used to have just 1 clone but separate build directories for different branches, but I would sometimes end up using the wrong build directory from a branch, which would end up wasting a lot of time (having only 1 clone also makes things more annoying when LLVM branches need to be switched).

I'm using sccache, but build times for a llvm RelWithDebInfoAssert + swift DebugAssert are still way too slow and swift RelWithDebInfoAssert misses too many variables... :grimacing: I try to avoid re-building the stdlib with a debug compiler if I can, but sometimes it can't be avoided...

5 Likes

It depends a bit on your workflow specifics if this would work for you, but I use some scripts described by this method Git: Stacked Commits | kastiglione.github.io that are backed by git worktree so that you always work on main, and you never switch branches in the repo because all PR submission / updating is done in a worktree instead. https://github.com/keith/dotfiles/blob/master/bin/git-submitpr

This might not work depending on how interdependent your changes are, or if they generally conflict and you can't build them all at once for some reason.

3 Likes

To follow up on Keith, I've been doing all my lldb work (which sometimes includes swift changes) using a single branch (usually main, but recently release/5.4). Each PR is a single commit on that branch.

The advantages are:

  • All work in progress is in one spot
  • No branch switching, and its impacts on build times or build directory management
  • Integration of work, when I run tests locally, I know that all my work in flight works together

I do have a second worktree I use for creating cherry-pick PRs, but I don't do work in it, don't do builds.

1 Like

Oh, I should add that I've recently been using RelWithDebInfo config. If I want full debug info, I try CCC_OVERRIDE_OPTIONS.

For example, to get debug info for one or more cpp files, you touch them, and then run ninja like so:

CCC_OVERRIDE_OPTIONS="# +-g O0" ninja ...

In this example, CCC_OVERRIDE_OPTIONS adds -g, and replaces -O with -O0. For more info about CCC_OVERRIDE_OPTIONS, see these comments: llvm-project/driver.cpp at a9b8270702a974af3389ddf28d87c2a5907a26a1 · apple/llvm-project · GitHub.

You can touch header files to rebuild any source file that imports it.

This doesn't always work, but it's nice when it does.

2 Likes

My workflows are a gigantic mess and you probably shouldn't emulate them, but since you asked:

  • I use only RelWithDebInfoAssert unless I specifically need something else. If the debugger has trouble giving me the info I need with a release build, I usually just write dump() calls directly into the compiler code and recompile. (The deprecation warnings when I build the compiler keep me from pushing those calls to GitHub.) Disgusting, I know, but debug compilers are painfully slow.

  • I typically have two working copies that are branched from main; one of them has a name like swift, the other has a name like swift-clean. The former is where I do long-term work (i.e. things that will take several days or weeks); the latter is where I do small fixes or make temporary changes to help diagnose a bug.

  • When I need to do work based on another branch (next, rebranch, release/5.3), I'll create a separate working copy for that branch. Sometimes I'll create a -clean variant too if I need one. (I don't do this if I'm just cherry-picking a change to another branch without building it, though—there, I just temporarily switch in the main working copy and then switch back.)

2 Likes

I build clang/LLVM once a day in the morning.

I'm usually working on 3-5 different git worktree branches, and 2-4 different swift configurations per branch. So I have around 10 different swift builds sharing one LLVM build. I build those dozens of times a day, usually just the swift compiler, but I always kick off both release and debug compilers because I need one to build libraries and run tests, and I need the other to, well, debug. Sometimes I build different configurations of the libraries, and less commonly build libraries for different targets. I can't have those those library builds requiring a compiler rebuild.

If I used the build-script to build, I'd never be doing anything but rebuilding LLVM. I setup my own build scripts for development that create symlinks to the LLVM build and copy libraries. A build script that forces you to rebuild all upstream comonents when you change branches or configurations and forces you to build libraries with a debug compiler is obviously not something a developer should be using. Our build-script is a CI tool that's being misused for development.

2 Likes