Extending Git

Git provides a few mechanisms to extend it. You can add shortcuts and create new commands, and add support for new transports; all without requiring to modify Git sources.

Command aliases for Git

There is one little tip that can make your Git command-line experience simpler, easier, and more familiar, namely, Git aliases. It is very easy in theory to create an alias. You simply need to create an alias.<command-name> configuration variable; its value is the expansion of alias.

One of the uses for aliases is defining short abbreviations for commonly used commands and their arguments. Another is creating new commands. Here are a couple of examples you might want to set up:

$ git config --global alias.co checkout
$ git config --global alias.ci commit
$ git config --global alias.lg log --graph --oneline --decorate
$ git config --global alias.aliases 'config --get-regexp ^alias.'

The preceding setup means that typing, for example, git ci would be the same as typing git commit. Aliases take arguments just as the regular Git commands do. Git does not provide any default aliases that are defining shortcuts for the common operations, unless you use a friendly fork of Git by Felipe Contreras: git-fc.

Arguments are split by space, the usual shell quoting and escaping is supported; in particular, you can use a quote pair ("a b") or a backslash (a b) to include space in a single argument.

Note

Note, however, that you cannot have the alias with the same name as a Git command; in other words, you cannot use aliases to change the behavior of commands. The reasoning behind this restriction is that it could make existing scripts and hooks fail unexpectedly. Aliases that hide existing Git commands (with the same name as Git commands) are simply ignored.

You might, however, want to run external command rather than a Git command in an alias. Or, you might want to join together the result of a few separate commands. In this case, you can start the alias definition with the ! character (with the exclamation mark):

$ git config --global alias.unmerged 
'!git ls-files --unmerged | cut -f2 | sort -u'

Because here the first command of the expansion of an alias can be an external tool, you need to specify the git wrapper explicitly, as shown in the preceding example.

Note

Note that in many shells, for example, in bash,! is the history expansion character and it needs to be escaped as !, or be within single quotes.

Note that such shell command will be executed from the top-level directory of a repository (after cd to a top level), which may not necessarily be the current directory. Git sets the GIT_PREFIX environment variable to the current directory path relative to the top directory of a repository, that is, git rev-parse --show-prefix. As usual, git rev-parse (and some git wrapper options) may be of use here.

The fact mentioned earlier can be used while creating aliases. The git serve alias, running git daemon to read-only serve the current repository at git://127.0.0.1/, makes use of the fact that the shell commands in aliases are executed from the top-level directory of a repo:

[alias]
serve = !git daemon --reuseaddr --verbose  --base-path=.  --export-all ./.git

Sometimes, you need to reorder arguments, use an argument twice, or pass an argument to the command early in the pipeline. You would want to refer to subsequent arguments as $1, $2, and so on, or to all arguments as $@, just like in shell scripts. One trick that you can find in older examples is to run a shell with a -c argument, like in the first of the examples mentioned next; the final dash is so that the arguments start with $1, not with $0. A more modern idiom is to define and immediately execute a shell function, like in the second example (it is a preferred solution because it has one of the fewer level of quoting, and lets you use standard shell argument processing):

[alias]
record-1 = !sh -c 'git add -p -- $@ && git commit' -
record-2 = !f() {  git add -p -- $@ && git commit }; f

Aliases are integrated with command-line completion. While determining which completion to use for an alias, Git searches for a git command, skipping an opening brace or a single quote (thus, supporting both of the idioms mentioned earlier). With modern Git (version 2.1 or newer), you can use the null command ":" to declare the desired completion style. For example, alias expanding to !f() { : git commit ; ... } f would use a command completion for git commit, regardless of the rest of the alias.

Git aliases are also integrated with the help system; if you use the --help option on an alias, Git would tell you its expansion (so you can check the relevant man page):

$ git co --help
'git co' is aliased to `checkout'

Adding new Git commands

Aliases are best at taking small one-liners and converting them into small useful Git commands. You can write complex aliases, but when it comes to larger scripts, you would probably like to incorporate them into Git directly.

Git subcommands can be standalone executables that live in the Git execution path (which you can find by running git --exec-path); on Linux, this normally is /usr/libexec/git-core. The git executable itself is a thin wrapper that knows where the subcommands live. If git foo is not a built-in command, the wrapper searches for the git-foo command first in the Git exec path, then in the rest of your $PATH. The latter makes it possible to write local Git extensions (local Git commands) without requiring access to the system's space.

This feature is what it makes possible to have user interface more or less integrated with the rest of Git in projects such as git imerge (see Chapter 7, Merging Changes Together) or git annex (see Chapter 9, Managing Subprojects - Building a Living Framework). It is also how projects such as Git Extras, providing extra Git commands, were made.

Note, however, that if you don't install the documentation for your command in typical places, or configure documentation system to find its help,, git foo --help won't work correctly.

Credential helpers and remote helpers

There is another place where simply dropping appropriately named executable enhances and extends Git. Remote helper programs are invoked by Git when it needs to interact with remote repositories and remote transport protocols Git does not support natively. You can find more about them in Chapter 5, Collaborative Development with Git.

When Git encounters a URL of the form <transport>://<address>, where <transport> is a (pseudo)protocol that is not natively supported, it automatically invokes the git remote-<transport> command with a remote and full remote URL as arguments. A URL of the form <transport>::<address> also invokes this remote helper, but with just <address> as a second argument in the place of a URL. Additionally, with remote.<remote-name>.vcs set to <transport>, Git would explicitly invoke git remote-<transport> to access that remote.

The helpers mechanism in Git is about interacting with external scripts using a well-specified format.

Each remote helper is expected to support a subset of commands. You can find more information about the issue of creating new helpers in the gitremote-helpers(1) man page.

There is another type of helpers in Git, namely, credentials helpers. They can be used by Git to get the credentials from the user required, for example, to access the remote repository over HTTP. They are specified by the configuration though, just like the merge and diff drivers, and like the clean and smudge filters.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset