Developing a Git Worktree Helper with Copilot

Over the past few weeks I’ve been developing and using a personal command-line tool called gwt (Git Worktree) to manage Git repositories using worktrees. This article explains what the tool does, how it evolved, and how I used GitHub Copilot CLI to develop it (in fact the idea of building the script was also to test the tool). The Problem: Managing Multiple BranchesI was working on a project with multiple active branches, including orphans; the regular branches are for fixes or features, while the orphans are used to keep copies of remote documents or store processed versions of those documents. The project also uses a special orphan branch that contains the scripts and the CI/CD configuration to store and process the external documents (it is on a separate branch to avoid mixing its operation with the main project code). The plan is trigger a pipeline against the special branch from remote projects to create or update the doc branch for it in our git repository, retrieving artifacts from the remote projects to get the files and put them on an orphan branch (initially I added new commits after each update, but I changed the system to use force pushes and keep only one commit, as the history is not really needed). The original documents have to be changed, so, after ingesting them, we run a script that modifies them and adds or updates another branch with the processed version; the contents of that branch are used by the main branch build process (there we use git fetch and git archive to retrieve its contents). When working on the scripts to manage the orphan branches I discovered the worktree feature of git, a functionality that allows me to keep multiple branches checked out in parallel using a single .git folder, removing the need to use git switch and git stash when changing between branches (until now I’ve been a heavy user of those commands). Reading about it I found that a lot of people use worktrees with the help of a wrapper script to simplify the management. After looking at one or two posts and the related scripts I decided to create my own using a specific directory structure to simplify things. That’s how I started to work on the gwt script; as I also wanted to test copilot I decided to build it using its help (I have a pro license at work and wanted to play with the cli version instead of integrated into an editor, as I didn’t want to learn a lot of new keyboard shortcuts). The gwt Philosophy: Opinionated and Transparentgwt enforces a simple, filesystem-visible model: Exactly one bare repository named bare.git (treated as an implementation detail)One worktree directory per branch where the directory name matches the branch nameSingle responsibility: gwt doesn’t try to be a general git wrapper; it only handles operations that map cleanly to this layout...

April 23, 2026 · 17 min · Sergio Talens-Oliag

GitLab CI/CD Tips: Automatic Versioning Using semantic-release

This post describes how I’m using semantic-release on gitlab-ci to manage versioning automatically for different kinds of projects following a simple workflow (a develop branch where changes are added or merged to test new versions, a temporary release/#.#.# to generate the release candidate versions and a main branch where the final versions are published). What is semantic-releaseIt is a Node.js application designed to manage project versioning information on Git Repositories using a Continuous integration system (in this post we will use gitlab-ci) How does it workBy default semantic-release uses semver for versioning (release versions use the format MAJOR.MINOR.PATCH) and commit messages are parsed to determine the next version number to publish. If after analyzing the commits the version number has to be changed, the command updates the files we tell it to (i.e. the package.json file for nodejs projects and possibly a CHANGELOG.md file), creates a new commit with the changed files, creates a tag with the new version and pushes the changes to the repository. When running on a CI/CD system we usually generate the artifacts related to a release (a package, a container image, etc.) from the tag, as it includes the right version number and usually has passed all the required tests (it is a good idea to run the tests again in any case, as someone could create a tag manually or we could run extra jobs when building the final assets …​ if they fail it is not a big issue anyway, numbers are cheap and infinite, so we can skip releases if needed). Commit messages and versioningThe commit messages must follow a known format, the default module used to analyze them uses the angular git commit guidelines, but I prefer the conventional commits one, mainly because it’s a lot easier to use when you want to update the MAJOR version. The commit message format used must be: <type>(optional scope): <description> [optional body] [optional footer(s)]...

December 26, 2023 · 14 min · Sergio Talens-Oliag

Using Git Server Hooks on GitLab CE to Validate Tags

Since a long time ago I’ve been a gitlab-ce user, in fact I’ve set it up on three of the last four companies I’ve worked for (initially I installed it using the omnibus packages on a debian server but on the last two places I moved to the docker based installation, as it is easy to maintain and we don’t need a big installation as the teams using it are small). On the company I work for now (kyso) we are using it to host all our internal repositories and to do all the CI/CD work (the automatic deployments are triggered by web hooks in some cases, but the rest is all done using gitlab-ci). The majority of projects are using nodejs as programming language and we have automated the publication of npm packages on our gitlab instance npm registry and even the publication into the npmjs registry. To publish the packages we have added rules to the gitlab-ci configuration of the relevant repositories and we publish them when a tag is created. As the we are lazy by definition, I configured the system to use the tag as the package version; I tested if the contents of the package.json where in sync with the expected version and if it was not I updated it and did a force push of the tag with the updated file using the following code on the script that publishes the package: # Update package version & add it to the .build-args INITIAL_PACKAGE_VERSION="$(npm pkg get version|tr -d '"')" npm version --allow-same --no-commit-hooks --no-git-tag-version \ "$CI_COMMIT_TAG" UPDATED_PACKAGE_VERSION="$(npm pkg get version|tr -d '"')" echo "UPDATED_PACKAGE_VERSION=$UPDATED_PACKAGE_VERSION" >> .build-args # Update tag if the version was updated or abort if [ "$INITIAL_PACKAGE_VERSION" != "$UPDATED_PACKAGE_VERSION" ]; then if [ -n "$CI_GIT_USER" ] && [ -n "$CI_GIT_TOKEN" ]; then git commit -m "Updated version from tag $CI_COMMIT_TAG" package.json git tag -f "$CI_COMMIT_TAG" -m "Updated version from tag" git push -f -o ci.skip origin "$CI_COMMIT_TAG" else echo "!!! ERROR !!!" echo "The updated tag could not be uploaded." echo "Set CI_GIT_USER and CI_GIT_TOKEN or fix the 'package.json' file" echo "!!! ERROR !!!" exit 1 fi fi...

August 1, 2022 · 7 min · Sergio Talens-Oliag