Git - MyFirstContribution Documentation (original) (raw)

Adding a New Command

Lots of the subcommands are written as builtins, which means they are implemented in C and compiled into the main git executable. Implementing the very simple psuh command as a built-in will demonstrate the structure of the codebase, the internal API, and the process of working together as a contributor with the reviewers and maintainer to integrate this change into the system.

Built-in subcommands are typically implemented in a function named "cmd_" followed by the name of the subcommand, in a source file named after the subcommand and contained within builtin/. So it makes sense to implement your command in builtin/psuh.c. Create that file, and within it, write the entry point for your command in a function matching the style and signature:

int cmd_psuh(int argc, const char **argv, const char *prefix)

We’ll also need to add the declaration of psuh; open up builtin.h, find the declaration for cmd_pull, and add a new line for psuh immediately before it, in order to keep the declarations alphabetically sorted:

int cmd_psuh(int argc, const char **argv, const char *prefix);

Be sure to #include "builtin.h" in your psuh.c. You’ll also need to#include "gettext.h" to use functions related to printing output text.

Go ahead and add some throwaway printf to the cmd_psuh function. This is a decent starting point as we can now add build rules and register the command.

Note Your throwaway text, as well as much of the text you will be adding over the course of this tutorial, is user-facing. That means it needs to be localizable. Take a look at po/README under "Marking strings for translation". Throughout the tutorial, we will mark strings for translation as necessary; you should also do so when writing your user-facing commands in the future.

int cmd_psuh(int argc, const char **argv, const char *prefix) { printf(_("Pony saying hello goes here.\n")); return 0; }

Let’s try to build it. Open Makefile, find where builtin/pull.o is added to BUILTIN_OBJS, and add builtin/psuh.o in the same way next to it in alphabetical order. Once you’ve done so, move to the top-level directory and build simply with make. Also add the DEVELOPER=1 variable to turn on some additional warnings:

$ echo DEVELOPER=1 >config.mak $ make

Note When you are developing the Git project, it’s preferred that you use theDEVELOPER flag; if there’s some reason it doesn’t work for you, you can turn it off, but it’s a good idea to mention the problem to the mailing list.

Great, now your new command builds happily on its own. But nobody invokes it. Let’s change that.

The list of commands lives in git.c. We can register a new command by adding a cmd_struct to the commands[] array. struct cmd_struct takes a string with the command name, a function pointer to the command implementation, and a setup option flag. For now, let’s keep mimicking push. Find the line wherecmd_push is registered, copy it, and modify it for cmd_psuh, placing the new line in alphabetical order (immediately before cmd_pull).

The options are documented in builtin.h under "Adding a new built-in." Since we hope to print some data about the user’s current workspace context later, we need a Git directory, so choose RUN_SETUP as your only option.

Go ahead and build again. You should see a clean build, so let’s kick the tires and see if it works. There’s a binary you can use to test with in thebin-wrappers directory.

$ ./bin-wrappers/git psuh

Check it out! You’ve got a command! Nice work! Let’s commit this.

git status reveals modified Makefile, builtin.h, and git.c as well as untracked builtin/psuh.c and git-psuh. First, let’s take care of the binary, which should be ignored. Open .gitignore in your editor, find /git-pull, and add an entry for your new command in alphabetical order:

... /git-prune-packed /git-psuh /git-pull /git-push /git-quiltimport /git-range-diff ...

Checking git status again should show that git-psuh has been removed from the untracked list and .gitignore has been added to the modified list. Now we can stage and commit:

$ git add Makefile builtin.h builtin/psuh.c git.c .gitignore $ git commit -s

You will be presented with your editor in order to write a commit message. Start the commit with a 50-column or less subject line, including the name of the component you’re working on, followed by a blank line (always required) and then the body of your commit message, which should provide the bulk of the context. Remember to be explicit and provide the "Why" of your change, especially if it couldn’t easily be understood from your diff. When editing your commit message, don’t remove the Signed-off-by trailer which was added by -s above.

psuh: add a built-in by popular demand

Internal metrics indicate this is a command many users expect to be present. So here's an implementation to help drive customer satisfaction and engagement: a pony which doubtfully greets the user, or, a Pony Saying "Um, Hello" (PSUH).

This commit message is intentionally formatted to 72 columns per line, starts with a single line as "commit message subject" that is written as if to command the codebase to do something (add this, teach a command that). The body of the message is designed to add information about the commit that is not readily deduced from reading the associated diff, such as answering the question "why?".

Signed-off-by: A U Thor author@example.com

Go ahead and inspect your new commit with git show. "psuh:" indicates you have modified mainly the psuh command. The subject line gives readers an idea of what you’ve changed. The sign-off line (-s) indicates that you agree to the Developer’s Certificate of Origin 1.1 (see theDocumentation/SubmittingPatches [[dco]] header).

For the remainder of the tutorial, the subject line only will be listed for the sake of brevity. However, fully-fleshed example commit messages are available on the reference implementation linked at the top of this document.

Implementation

It’s probably useful to do at least something besides printing out a string. Let’s start by having a look at everything we get.

Modify your cmd_psuh implementation to dump the args you’re passed, keeping existing printf() calls in place:

int i;

...

printf(Q_("Your args (there is %d):\n",
      "Your args (there are %d):\n",
      argc),
       argc);
for (i = 0; i < argc; i++)
    printf("%d: %s\n", i, argv[i]);

printf(_("Your current working directory:\n<top-level>%s%s\n"),
       prefix ? "/" : "", prefix ? prefix : "");

Build and try it. As you may expect, there’s pretty much just whatever we give on the command line, including the name of our command. (If prefix is empty for you, try cd Documentation/ && ../bin-wrappers/git psuh). That’s not so helpful. So what other context can we get?

Add a line to #include "config.h". Then, add the following bits to the function body:

const char *cfg_name;

...

git_config(git_default_config, NULL);
if (git_config_get_string_tmp("user.name", &cfg_name) > 0)
    printf(_("No name is found in config\n"));
else
    printf(_("Your name: %s\n"), cfg_name);

git_config() will grab the configuration from config files known to Git and apply standard precedence rules. git_config_get_string_tmp() will look up a specific key ("user.name") and give you the value. There are a number of single-key lookup functions like this one; you can see them all (and more info about how to use git_config()) in Documentation/technical/api-config.adoc.

You should see that the name printed matches the one you see when you run:

$ git config --get user.name

Great! Now we know how to check for values in the Git config. Let’s commit this too, so we don’t lose our progress.

$ git add builtin/psuh.c $ git commit -sm "psuh: show parameters & config opts"

Note Again, the above is for sake of brevity in this tutorial. In a real change you should not use -m but instead use the editor to write a meaningful message.

Still, it’d be nice to know what the user’s working context is like. Let’s see if we can print the name of the user’s current branch. We can mimic thegit status implementation; the printer is located in wt-status.c and we can see that the branch is held in a struct wt_status.

wt_status_print() gets invoked by cmd_status() in builtin/commit.c. Looking at that implementation we see the status config being populated like so:

status_init_config(&s, git_status_config);

But as we drill down, we can find that status_init_config() wraps a call to git_config(). Let’s modify the code we wrote in the previous commit.

Be sure to include the header to allow you to use struct wt_status:

Then modify your cmd_psuh implementation to declare your struct wt_status, prepare it, and print its contents:

struct wt_status status;

...

wt_status_prepare(the_repository, &status);
git_config(git_default_config, &status);

...

printf(_("Your current branch: %s\n"), status.branch);

Run it again. Check it out - here’s the (verbose) name of your current branch!

Let’s commit this as well.

$ git add builtin/psuh.c $ git commit -sm "psuh: print the current branch"

Now let’s see if we can get some info about a specific commit.

Luckily, there are some helpers for us here. commit.h has a function calledlookup_commit_reference_by_name to which we can simply provide a hardcoded string; pretty.h has an extremely handy pp_commit_easy() call which doesn’t require a full format object to be passed.

Add the following includes:

#include "commit.h" #include "pretty.h"

Then, add the following lines within your implementation of cmd_psuh() near the declarations and the logic, respectively.

struct commit *c = NULL;
struct strbuf commitline = STRBUF_INIT;

...

c = lookup_commit_reference_by_name("origin/master");

if (c != NULL) {
    pp_commit_easy(CMIT_FMT_ONELINE, c, &commitline);
    printf(_("Current commit: %s\n"), commitline.buf);
}

The struct strbuf provides some safety belts to your basic char*, one of which is a length member to prevent buffer overruns. It needs to be initialized nicely with STRBUF_INIT. Keep it in mind when you need to pass around char*.

lookup_commit_reference_by_name resolves the name you pass it, so you can play with the value there and see what kind of things you can come up with.

pp_commit_easy is a convenience wrapper in pretty.h that takes a single format enum shorthand, rather than an entire format struct. It then pretty-prints the commit according to that shorthand. These are similar to the formats available with --pretty=FOO in many Git commands.

Build it and run, and if you’re using the same name in the example, you should see the subject line of the most recent commit in origin/master that you know about. Neat! Let’s commit that as well.

$ git add builtin/psuh.c $ git commit -sm "psuh: display the top of origin/master"

Adding Documentation

Awesome! You’ve got a fantastic new command that you’re ready to share with the community. But hang on just a minute - this isn’t very user-friendly. Run the following:

$ ./bin-wrappers/git help psuh

Your new command is undocumented! Let’s fix that.

Take a look at Documentation/git-*.adoc. These are the manpages for the subcommands that Git knows about. You can open these up and take a look to get acquainted with the format, but then go ahead and make a new fileDocumentation/git-psuh.adoc. Like with most of the documentation in the Git project, help pages are written with AsciiDoc (see CodingGuidelines, "Writing Documentation" section). Use the following template to fill out your own manpage:

git-psuh(1)

NAME

git-psuh - Delight users' typo with a shy horse

SYNOPSIS

[verse] 'git-psuh [...]'

DESCRIPTION

...

OPTIONS[[OPTIONS]]

...

OUTPUT

...

GIT

Part of the git[1] suite

The most important pieces of this to note are the file header, underlined by =, the NAME section, and the SYNOPSIS, which would normally contain the grammar if your command took arguments. Try to use well-established manpage headers so your documentation is consistent with other Git and UNIX manpages; this makes life easier for your user, who can skip to the section they know contains the information they need.

Note Before trying to build the docs, make sure you have the package asciidocinstalled.

Now that you’ve written your manpage, you’ll need to build it explicitly. We convert your AsciiDoc to troff which is man-readable like so:

$ make all doc $ man Documentation/git-psuh.1

or

$ make -C Documentation/ git-psuh.1 $ man Documentation/git-psuh.1

While this isn’t as satisfying as running through git help, you can at least check that your help page looks right.

You can also check that the documentation coverage is good (that is, the project sees that your command has been implemented as well as documented) by runningmake check-docs from the top-level.

Go ahead and commit your new documentation change.

Adding Usage Text

Try and run ./bin-wrappers/git psuh -h. Your command should crash at the end. That’s because -h is a special case which your command should handle by printing usage.

Take a look at Documentation/technical/api-parse-options.adoc. This is a handy tool for pulling out options you need to be able to handle, and it takes a usage string.

In order to use it, we’ll need to prepare a NULL-terminated array of usage strings and a builtin_psuh_options array.

Add a line to #include "parse-options.h".

At global scope, add your array of usage strings:

static const char * const psuh_usage[] = { N_("git psuh [...]"), NULL, };

Then, within your cmd_psuh() implementation, we can declare and populate ouroption struct. Ours is pretty boring but you can add more to it if you want to explore parse_options() in more detail:

struct option options[] = {
    OPT_END()
};

Finally, before you print your args and prefix, add the call toparse-options():

argc = parse_options(argc, argv, prefix, options, psuh_usage, 0);

This call will modify your argv parameter. It will strip the options you specified in options from argv and the locations pointed to from optionsentries will be updated. Be sure to replace your argc with the result fromparse_options(), or you will be confused if you try to parse argv later.

It’s worth noting the special argument --. As you may be aware, many Unix commands use -- to indicate "end of named parameters" - all parameters after the -- are interpreted merely as positional arguments. (This can be handy if you want to pass as a parameter something which would usually be interpreted as a flag.) parse_options() will terminate parsing when it reaches -- and give you the rest of the options afterwards, untouched.

Now that you have a usage hint, you can teach Git how to show it in the general command list shown by git help git or git help -a, which is generated fromcommand-list.txt. Find the line for git-pull so you can add your _git-psuh_line above it in alphabetical order. Now, we can add some attributes about the command which impacts where it shows up in the aforementioned help commands. The top of command-list.txt shares some information about what each attribute means; in those help pages, the commands are sorted according to these attributes. git psuh is user-facing, or porcelain - so we will mark it as "mainporcelain". For "mainporcelain" commands, the comments at the top ofcommand-list.txt indicate we can also optionally add an attribute from another list; since git psuh shows some information about the user’s workspace but doesn’t modify anything, let’s mark it as "info". Make sure to keep your attributes in the same style as the rest of command-list.txt using spaces to align and delineate them:

git-prune-packed plumbingmanipulators git-psuh mainporcelain info git-pull mainporcelain remote git-push mainporcelain remote

Build again. Now, when you run with -h, you should see your usage printed and your command terminated before anything else interesting happens. Great!

Go ahead and commit this one, too.