Refactoring the Command-Line interface

We'll refactor the command-line interface of multi-git into its proper place in our directory structure in this lesson.

In this lesson, we will not make changes to the functionality. We’ll just move files around. This measured approach of separating refactoring from functional changes helps us eliminate many problems and if something does go wrong, it is easier to identify the root cause and fix it.

Planning the refactoring

In the previous lesson, we refactored the core logic into a set of packages under the pkg top-level directory. Now, we will do something similar for the command-line interface. We will move into a new subdirectory called mg, which is short for multi-git under the cmd top-level directory. This follows established conventions in the Go community. It will allow adding additional commands later if necessary.

The plan is as follows:

  • Create the multi-git command
  • Move the code from the main() function into the new command
  • Verify that everything still works

Let’s get to it!

Creating the command

Creating the command is as simple as creating the top-level directory, cmd, and a sub-directory called, mg:

$ mkdir -p cmd/mg

Here is our new directory structure:

$ tree -L 2
.
├── LICENSE
├── README.md
├── cmd
│   └── mg
├── go.mod
├── go.sum
└── pkg
    ├── helpers
    └── repo_manager

The pkg directory contains two packages: repo_manager and helpers. The new cmd directory contains the mg directory.

Moving the code from the main() function

Moving the code from the main() function is trivial. All we have to do is move the entire main.go file, as is, to the cmd/mg directory.

$ mv main.go cmd/mg

To build the mg command we need to run the following commands:

$ cd cmd/mg
$ go build

The result is the mg binary.

$ ls -laHG
total 4888
drwxr-xr-x  4 gigi.sayfan  staff      128 Nov  3 09:45 .
drwxr-xr-x  3 gigi.sayfan  staff       96 Nov  3 09:22 ..
-rw-r--r--  1 gigi.sayfan  staff      786 Nov  3 09:28 main.go
-rwxr-xr-x  1 gigi.sayfan  staff  2498264 Nov  3 09:45 mg

The reason it works with no changes is that the code in the main() function imports the repo_manager package by its fully qualified path and not as a relative path. The location of the main package itself is not important.

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"path"
	"strings"

	"github.com/the-gigi/multi-git/pkg/repo_manager"
.
.
.
)

Verifying everything works

This version of multi-git is tagged as v0.3 and is available here: https://github.com/the-gigi/multi-git/releases/tag/v0.3

Let’s have some fun with the interactive terminal. Now, that you’re in the driver’s seat, play around with multi-git. In the terminal below git and go are already installed. You will fetch the code for multi-git, build it, create a few directories and git repos, and let mg run git commands against the repositories.

First, to get multi-git type the following commands:

mkdir test-mg
cd test-mg
git clone --branch v0.3 https://github.com/the-gigi/multi-git

Then, to build the mg, executable type the following commands:

cd multi-git/cmd/mg
go build
mv mg /usr/local/bin

Feel free along the way to type ls to see what files exist in each directory. In particular, there should be the mg executable in the current directory (/test-mg/multi-git/cmd/mg) after the go build command. The mg executable is then moved to the /usr/loca/bin directory.

The next step is to create a couple of directories that will contain the repositories multi-git will operate on. If you want to follow my footsteps, use:

mkdir -p /test-mg/repos/repo-1
mkdir -p /test-mg/repos/repo-2

Now would be a good time to set MG_ROOT and MG_REPOS:

export MG_ROOT=/test-mg/repos
export MG_REPOS=repo-1,repo-2

And… that’s it. You’re ready to test multi-git interactively. Your first command should be

mg --command init

This will initialize git in the target repositories. Then, you can freestyle, check that mg indeed created a .git subdirectory in repo-1 and repo-2, add files and directories, and issue more mg commands.

Note that if you want to commit files, you’ll have to configure git with your username and email

git config --global user.email "you@example.com"
git config --global user.name "Your Name"

Get hands-on with 1200+ tech skills courses.