How to write portable Git library scripts?
I create custom Git commands by writing scripts in /usr/local/libexec/git
and creating aliases to invoke them. For example:
git config --global alias.graph '!/usr/local/libexec/git/git-graph'
The tricky part is that I want my scripts to be "portable" in the sense that anyone who has installed Git should be able to install and run my scripts the same way I do, without encountering missing dependencies.
Important Edit: Following @Schwern's response, I realize I must add a bit more information. Replace "anyone who has installed Git" with "anyone who interacts with Git through the git-core
scripts provided by the official Git sources". For example, invoking git rebase
executes .../git-core/git-rebase.sh
. (You can click that last word; it's a hyperlink, but it doesn't appear that way in all browsers.) I assume this is the situation for most Git users, as this is the default provided by the Git project itself.
From my years of experience writing portable shell scripts, I know it's futile to expect a shell script of even moderate complexity to run on every Unix-like OS that ever existed. However, I think it's reasonable to expect a common base of utilities to exist between Git workstations, at least enough to avoid constantly jumping through hoops and walking on eggshells when writing scripts to run on these workstations.
Here are my assumptions about the Git workstations:
git-core
directory (eg /usr/libexec/git-core
) have not been altered since being placed there by the Git installation. git-core
run as expected (ie same as on the Git maintainers' workstations, or anyone with a "normal" setup). How can I determine the common base of utilities I can use to write portable scripts on these Git workstations?
Edit: I am basically looking for a list of dependencies for the git-core
scripts provided in the main Git distribution. This will give the common base I am looking for.
In fact, all of Git's built in scripts (there are many, mostly sh
and a few perl
) live in that git-core
directory. Running git xxx
attempts to execute program git-xxx
, typically out of the git-core
directory, after setting up various environment variables. For instance, git --git-dir=/path/to/some/dir
exports a GIT_DIR=/path/to/some/dir
setting.
The precise location of the git-core
directory itself is set when you build Git. You can view it by running:
git --exec-path
Note that while git foo
will try to run $(git --exec-path)/git-foo
, if there is no git-foo
in there and you put git-foo
in your own scripting directory (that is somewhere on your $PATH
), git foo
will end up running that. The mechanism is straightforward: the git
front end inserts the git-core path into the front of $PATH
:
$ cat << 'end' > $HOME/scripts/git-statusy
? #! /bin/sh
? echo statusy: $PATH is $PATH
? git status
? 'end'
$ chmod +x $HOME/scripts/git-statusy
$ git statusy
statusy: $PATH is /usr/local/libexec/git-core:[...snipped]
On branch master
... [snipped]
Within the git-core
directory, there is a file named git-sh-setup
(qv). This contains some convenience functions and some workarounds for known issues on systems for which Git is built. A "core" Git script should therefore always begin with . git-sh-setup
. git-sh-setup
. (This works because $PATH
has the git-core directory at the front.) Some "non-core" contrib scripts explicitly run . $(git --exec-path)/git-sh-setup
. $(git --exec-path)/git-sh-setup
instead though.
Within your script, you can depend on any POSIX shell built-ins, except that you need to work around certain bugs (eg, FreeBSD /bin/sh mishandles return
in some versions). Support for additional commands is basically on an "as found by testing" basis, as far as I know: if you look at the history of git-sh-setup.sh
and other .sh
files in the Git source, you will see various changes made over time to deal with such things.
There's no such thing as a "Git workstation" anymore than there's an "SVN workstation" or even a "Ruby workstation". It's just some machine that has Git installed and is being used to work on or obtain some project that uses Git. If they're in a Windows environment, it will be Windows. If they're on an outdated Solaris machine, it will be a Solaris environment. Since Git now comes standard with OS X, every OS X machine is a "Git workstation".
Because "Git workstation" includes Windows, and Windows doesn't come with any of the normal shell utilities, you can't use shell at all. At best you can rely on the minimal set that comes with Git Bash, but there are plenty of Windows/Git installations that don't use Git Bash. Instead, a lot of IDEs come with Git bundled in them.
None of this changes the compatibility challenges of writing portable shell scripts. BSD vs Gnu vs some commercial monstrosity, and all with the problem of what about Windows, that will all still be there.
None of this changes being polite about installing 3rd party extensions. They don't go into /usr
, that's for system stuff and should remain unmodified. They go in something like /usr/local
or even ~/bin
. Git scripts will work if they are in the user's PATH
.
As to some of your assumptions about Git...
All scripts in the git-core directory (eg /usr/libexec/git-core) have not been altered since being placed there by the Git installation.
That's a fair assumption... if it exists. Here on OS X there is no /usr/libexec/git-core
and I can't find any equivalent. I suspect they're all compiled into the binary. You can't assume Git is a pile of scripts or even has compatibility hard links like MacPorts does.
All scripts in git-core run as expected (ie same as on the Git maintainers' workstations, or anyone with a "normal" setup).
Again, that's a fine assumption if they exist which they don't have to.
The installed version of Git meets some arbitrary minimum requirement that I have decided to support with my scripts.
Rather than being arbitrary, pick the minimum version that supports the features your scripts need and that isn't too inconvenient for you. Always check it with git --version
.
With all that in mind, to be portable...
git
directly, not via scripts. git
. 上一篇: 如何让Apache启动?
下一篇: 如何编写可移植的Git库脚本?