Building a Waiting git Editor
When I commit to a git repository, I don’t like using a command line editor, but using a GUI editor sometimes takes a while to boot up. I’ve settled on using TextWrangler for now since it opens a window reasonably fast, especially if the app is already open.
I always thought it would be cool to build a dedicated git editor that you can launch from the command line, see a nice diff of my changes, and edit using native Mac text editing. If I ever want to do that, I need to figure out how git creates a commit with an external editor.
My git config for the editor looks like this:
$ git config --get core.editor edit --wait --new-window
Git will open TextWrangler using a couple of options:
--wait— waits for the window to close before returning
--new-window— makes sure a new window is created for commit messages instead of merging this with an already open window
--new-window makes sense to me, but how does
--wait work? Well, a bunch of things happen when you type
- git checks its editor config to find out which command to use
- That editor command is called with a file path at the end
- git also notes the pid of the editor when it opens
- The file path passed is a path to
COMMIT_EDITMSGlocated inside the project’s local .git directory
- The editor makes its edits and closes its pid
- git sees that the pid from earlier has finished, reads
COMMIT_EDITMSG, parses it to remove comments, and finally takes the final result and stores it as a commit message
(I’m sure it does much more than just that, but this is all I need to know for now.)
Great, so now all I have to do is make an editor that writes text and closes its process when its done. A ruby script will exit automatically when its done running, so if I can create a script to write some text to a file, it should work.
Here’s what I came up with:
#!/usr/bin/env ruby commit_message_path = ARGV.last puts File.read(commit_message_path) print 'Commit Message: ' message = $stdin.gets File.write(commit_message_path, message)
The first line uses a hashbang to tell the rest of the script to be interpreted as a ruby program.
Then, I grab the last argument passed into the program and print all its contents out to the screen. That way I can see all the changes that have happened since the last commit. This printing is optional.
Next, I give the user a prompt to input their commit message. Once they hit return, I write the commit message to the original file. The ruby process closes, git does its thing, and I have a new commit message!
This is obviously very primitive. It’s still a command line UI, not a GUI. It can’t do multiline strings. In fact, it’s less useful than using the
-m option with
git commit. But now I know (and so do you) how to build a waiting editor.