PBS 116 — Git: Team Git
At the start of our Git series-within-a-series I promised to take you on a journey through four scenarios — using a single git repo, using multiple git repos, but not shared with others, sharing repos with a team, and contributing to the open source community. So far we’ve covered the first two — working alone in a single repo, and working alone with multiple repos. It turns out that technologically there’s no difference in working alone with multiple repos and working with a team of peers using a shared collection of repos, but there are some very important philosophical considerations, so we’ll focus on those in this instalment. That will then set us up to move on to our final scenario — contributing to the open source community.
Matching Podcast Episode
Listen along to this instalment on episode 685 of the Chit Chat Across the Pond Podcast.
You can also Download the MP3
Fetch -v- Pull
Before we look at working with Git as part of a team, I want to take a moment to underline the difference between fetching and pulling.
Remember that Git keeps a local cache of all defined remotes. Fetching is simply the act of updating that local cache. No changes are merged into any of your local branches, or even your working tree. This means that fetching can never trigger any kind of conflict.
Pulling on the other hand merges the remote branch into the local branch. That’s right, a pull is effectively a merge! So, like any other merge, a pull can result in a merge conflict, and if it does, you resolve it exactly like you would any other merge conflict. If you need a refresher, instalment 110 is entirely dedicated to dealing with merge conflicts.
Working as a Team
To get a team up and running you need to handle the both the practicalities of somehow hosting a shared repository, and, of coordinating its use so as to avoid chaos.
It might sound more daunting to get some kind of shared repo up and running, but that’s definitely the easy part, it’s the coordination that requires the challenging mix of people skills, logic, practicality, and flexibility!
The Big Picture
The mental model when working as a team is very similar to the scenario of a lone developer with multiple computers we’ve used in recent instalments.
There will be one repository accessible to the entire team that is agreed to be the primary repo, i.e. the ultimate source of truth. Each developer will have a clone of that primary repo on each of their devices, and they’ll do all their coding locally, pushing and pulling commits to and from the primary repo as they work.
When it comes to setting up a repository everyone can access there are lots of options, and they all have their plusses and minuses depending on your needs, skills, and resources. Some are easy to set up, some take a lot of work, some provide very basic functionality, some have more bells and whistles than you can shake a stick at, some are free, and some come with a very substantial bill indeed. Each option is a good fit for some team somewhere, and there are more options than I could possibly list. The sky is very much the limit!
With that caveat in mind, here are a few commonly used options:
- A bare Git repo on a NAS everyone on the team has mounted on their local file system.
- A bare Git repo on a computer/server everyone on the team has SSH access to.
- A server running the Community Edition of GitLab
- A free or paid-for package on a cloud-hosted Git-as-a-Service offering, e.g.:
All the cloud-hosted offerings give you much more than just plain Git. At the very least they all offer options for documenting your code, tracking issues/feature requests, and even running automations triggered by commits. Many also offer additional features like integrated basic websites, and advanced automation pipelines (CI/CD e.g. Continuous Integration/Continuous Design) for automatically testing and deploying code to staging and production environments.
Within corporate organisations the paid-for plans probably make the most sense. There’s more likely to be a need for the more advanced features, and, dedicated support is a must for anything business-critical, and for many business, that very much includes their code!
But, for a group of friends working together on something, those high-end plans are total over-kill. The free offerings from all the major players should work just fine, though I am a particular fan of free GitHub organisations. This entire series, and its sibling series Taming the Terminal, are hosted in a free GitHub organisation (Bartificer Creations).
Getting your Team Organised
In my experience, for things to go well you need to agree a number of things before you start using a shared repo. Just getting started and then trying to figure things out later is a recipe for disaster, trust me!
Before you write a single line of code, all contributors need to be on the same page on all of these points at the very least:
Coding style — spaces or tabs? How many? Do brackets/braces get cuddled, where do the opening and closing braces go? And much much more. FWIW, my advice is to agree to use the official, or the generally accepted, style guide for each language you’ll use.
- Development paradigm — will you be using the test-driven-development? Or some other software engineering approach?
- Branching strategy (see instalment 107)
- Commit Message Policy — an agreed approach for writing meaningful commit messages. Rather than re-inventing the wheel, it’s probably best to adopt a standard approach like Conventional Commits as described in instalment 109.
- Versioning Policy — an agreed version naming scheme, ideally a widely used standard of defacto-standard like SemVer (also described in instalment 109)
Of all those things, only one can easily be changed later, the branching strategy. That one you can easily tweak and tune without incurring technical debt, but all the others become ever more time-consuming to retro-fit as the commits roll in!
When finessing the branching policy I’d suggest focusing on minimising the amount of time developers spend resolving merge conflicts by minimising needless branch-sharing by developers. If everyone is free to work away on their own thing in peace things are likely to run more smoothly! That probably means breaking the work down into well defined tasks, and using a separate dev branch for each task.
Working as Part of a Team
My advice here is simple — fetch early and fetch often, and if you’re sharing a branch with someone, be considerate with your pushes so they can pull with confidence. Don’t push known broken code into a colleague’s proverbial lap without at least marking the commit to that effect in some way, or, failing that, messaging your colleagues out-of-band (Slack, Teams, Discord, what ever)!
Ideally each time the code is in a sane state it should be committed and pushed, and colleagues should feel safe pulling any new commits that show up. A sure-fire way to get lots of nasty conflicts is to have two people working on the same branch and only pushing and pulling after hours and hours of work!
Note that the definition of a sane state is very fluid, and will vary a lot from project to project. If the branch was created to add a complex new function then a sane state would allow the function being built to be broken, but the app as a whole should probably still compile/run!
Ultimately, working as a team means working with other human beings. Git can be a great tool that facilitates really enjoyable and fruitful collaboration, but it by no means guarantees that! Using Git is not a substitute for using the usual kinds of messaging and communications apps that keep teams on the same page. Taking 15 seconds to let a colleague know about a bug you’ve just been forced to commit because you don’t have time to fix it before you have to run off to something will save them tripping over your bug unexpectedly next time they pull! Like with any other team, try give your colleagues the benefit of the doubt — if there’s two ways to read the tone of a comment, assume the most benign!
Final Thoughts — The Open Source Community is not a Team
At this stage we’ve discussed using a single repo, then a lone developer with multiple repos, and now a team with multiple repos. The last scenario we promised to cover in this series-within-a-series is contributing to open source projects. You might assume that’s no different to working on a team, but that’s usually not the case. An open source project will have a core group of so-called maintainers, and they work as a team, but open source code doesn’t just come from the core team, it also comes from random strangers, that’s the whole point!
In a team environment you can set up permissions to match people’s roles, and then they simply work away, but a more complex workflow is needed to accept code from strangers. For both security and reliability reasons code submitted to an open source project needs to be vetted before it’s merged into the project’s official repo. That requires a workflow we’ve not covered yet, and, in most cases, extending Git beyond its core features.
There’s more to Git than Git
Git was designed to be extensible, and to be plugged in to larger systems. As well as the command line interface we’ve been using, there are also Git APIs in many programming languages. This allows developers to embed Git into larger systems, and, to add custom features to Git that are not part of the core version.
The people behind GitHub have done the latter — there are things GitHub can do that Git can’t, and some of those extended features help power the open source world. Not all open source projects are hosted on Git, but a heck of a lot of them are, and that’s because GitHub provides an excellent workflow for offering code changes to open source projects — fork, edit, then send a pull request.
Neither forking nor pull requests are standard Git features — a fork is an enhanced clone, and a pull request a special kind of push.
Join the Community
Find us in the PBS channel on the Podfeet Slack.