Assemblies best practices in git for hardware
Most electronics designs are more complicated than a single PCBA. Very often, designs are compartmentalized into multiple interconnected PCBAs, wiring harnesses, enclosures, mechanical fasteners, labels, packaging, and anything else necessary to run the design.
Creating a repository for each PCBA makes a lot of sense to track each PCBA’s files independently, but what are your options and best practices when you want to start creating final assemblies and integrations of the various subassemblies?
One of the simplest methods of organizing your assemblies is to leave it out of the revision control tool. You can use something as fancy as a PLM system, or as low-tech as a text file or spreadsheet to keep track of what PCBs are part of a sub-system and how they interconnect.
The downside of this approach is that none of the interconnections are kept in revision control. Cables can be come quickly outdated, and it’s easy to route the wrong signals between boards. If PCBAs aren’t synced, the pinouts from one PCB might not match the connections on a linked board. It’s easy to create simple misunderstandings like flipping the pin rows, or confusing which is pin 1 and what direction the pin numbering increments.
It’s also easy for circuit boards to become out of lock-step with one another. Changes to one PCB might be introduced that make it no longer compatible with other PCBs in the upper level assembly. Sometimes it’s necessary to fork a PCB with older revisions being produced and used in parallel with new changes.
The next simplest method of organizing repos is using folders on your local machine. Each folder consists of an upper level assembly with the subfolders being subassembly repos. This helps group the correct revision boards with one another without much extra overhead.
This can solves the problem of using a PCBA from two different releases in two different situations. One upper level assembly contains a repo checked out to one version of release, and another upper level assembly can contain a subfolder with the repo checked out to a different release. Both folders can co-exist with changes being added to the separate branches without affecting the upper level design.
This method is more complicated when the same version of a repo is used in multiple locations. Each subfolder would need to be cloned, fetched, and pulled separately. It is very easy to lose sight of which folder is the working copy when you need to make a change to the repo once and pull the changes to every instance of that folder.
The other main drawbacks to this method is that the organization is external to any system. It lives on a single person’s local machine, and isn’t viewable on anyone else’s machine with out synchronizing. It’s not incredibly reliable or robust and has no way of ensuring the design hierarchy. It is, however simple.
Although we don’t recommend git subtrees, we should mention that it’s a slightly more formal version of using folders. Subtrees allow you to clone a repo in a subfolder of another repo. It doesn’t add any new metadata files. This means that the subtree files are mixed in with your upper level repo and it won’t be readily apparent to others.
If you make commits to the subtree repo, they will be stored in the host commits. It is extremely easy to confuse the two and make commits that span the subtree and the upper level host folder. It is up to the user to separate the commits to each subtree. It also requires a commit that is explicit from the subfolder to the subtree repo. Because of the non-obvious project organization and the chance for cross-committing, we cannot recommend this approach.
If you do pursue subtrees, know that there is a difference between subtree merging strategy and the subtrees command.
Submodules have a bad rap as being overly complicated, or difficult to learn, but they are the simplest and most robust tool for managing repos with subrepos that behave as you would expect them to. Creating a submodule will clone a repo in a subfolder and pin it to a specific commit. It will also maintain the git files so that changes to the repo can be pushed as well as pulled, although we recommend only using the submodule as a read-only reference.
We recommend creating a repo for each level of an upper level assembly and then clone submodules into subfolders to show the subassembly hierarchy. Although it’s possible to use these submodules to make edits and commits to the subassemblies, we recommend using these repos to keep track of upper level assemblies and only pull committed changes as the submodules change and write the changes to another folder, elsewhere.
In order to keep the changes to submodules separate, it is recommended to clone a separate working repo in a different folder from the upper level assembly. Edits to the submodule are commit and pushed from this working directory. When the upper level assembly is ready to accept these new changes, the user pulls the changes from the submodule in the assembly subfolder.
By separating the working directory and the assembly directory, you allow for users to work simultaneously on the subassembly independently of the upper level assembly. You can fix bugs, add features, or repurpose an existing PCBA design without altering the assembly hierarchy. Likewise, you can specify a hierarchy of PCBAs without putting a block on changes to any of the subassemblies.
In the following example, we show the folder Assembly5678 in yellow, containing a subfolder submodule of PCBA1234. The only changes that should be commit are in relation to how the assembly is put together, i.e. all the subfolders. If you wish to create changes for PCBA1234, we recommend cloning that repo to a separate folder that is not nested inside the assembly directory.
The benefit of using this approach is that you have assemblies that are obvious to everyone. The local filesystem matches the remote filesystem. Everyone who views the upper level assembly in the repo will be able to tell which repos are submodule repos and will point to the exact intended commit. By moving the editable working directory to an external directory, you separate the changes from the subassembly and the upper level assembly, gaining revision control without worrying about changes to the assembly affecting the nested subassembly.
It might be useful to think of the work-in-progress repos as being a library where the files are edited, and the assembly is where the references to the library are checked out for other applications to use.
If you’re interested in trying out submodules, we have a how-to guide.
Because most electronic designs contain a hierarchy of components, it is important to be able to specify how the components fit together while continuing to edit and refine the source files. Whether you use an external system to keep track of the relationships, nested folders, or nested repos, you have a lot of flexibility to create assemblies in a way that makes sense to your team.