It's true. It really is. I just spent an hour writing an Applescript to keep me from having to do the same set of commands over and over and over again.
The Problem: I have seven pieces of software that I like to keep up to date with the bleeding edge. To do so I have to do the following:
- Open a new Terminal.app tab
- Type in "cd <directory>"
- Remember if it's a git or svn repository
- Type in either "git pull" or "git svn rebase"
- Once the update is complete, notice if there are any updates. If not, stop.
- If there have been, optionally push those changes to a mirror with "git push"
- List the changes using "git changes" (an alias I've set up in my ~/.gitconfig)
The Solution: A new AppleScript application:
#!/usr/bin/osascript global win set win to missing value to update of directory given pull:pullCommand, push:remote tell application "Terminal" tell application "System Events" to set process "Terminal"'s frontmost to true set t to missing value if win is missing value then tell application "System Events" to ¬ tell process "Terminal" to ¬ keystroke "n" using command down set win to the front window else set win's frontmost to true tell application "System Events" to ¬ tell process "Terminal" to ¬ keystroke "t" using command down end if set t to win's selected tab
The above hack is brought to you by the fact that the obvious AppleScript "set win to make new window" doesn't work. Terminal.app doesn't know how to "make new window" or "make new tab". APPLE FAIL.
set command to "clear && echo " & directory & " && cd ~/dev/" & directory ¬ & " && i=\"$(git rev-parse HEAD)\" &&" & pullCommand ¬ & "&& [ \"$i\" != \"$(git rev-parse HEAD)\" ]" if remote is true then set command to command & "&& git push" else if remote is not false then set command to command & "&& git push " & remote end if set command to command & " && git changes" do script command in t end tell end update
The above essentially boils down to the following shell commands (concatenated with "&&" so it stops at the first command that fails):
- Clears the screen
- echo <directory>
- Shows me which directory we're working in. Usually this is repeated in the title bar, but I put this in just for safety.
- cd ~/dev/<directory>
- I keep all my development work in "~/dev", which a directory per project.
- i="$(git rev-parse HEAD)"
- Keep around the current value of HEAD, which is git's current state. Lets me check later to see if we got any changes.
- <pull command>
- This will be either "git pull" or "git svn rebase" based on how this is called.
- [ "$i" != "$(git rev-parse HEAD)" ]
- Aforementioned checking for changes. Because of the &&s if this is not true (i.e. HEAD before pull is the same as HEAD after), nothing after this will run if it didn't download any changes.
- git push <remote>
- This is only added if a remote is passed in. If the remote is true, this is just "git push" which pushes to the default remote for the branch.
- git changes
- This is a custom alias that does "git log --reverse --no-merges ORIG_HEAD..HEAD | tig". This prints all non-merge commits (since those shouldn't have functional changes) between ORIG_HEAD (state before pulling) and HEAD in chronological order (default is newest first, so --reverse is oldest first). tig is a pager designed for git, which lets me show patches for commits, etc etc.
to git at directory update of directory without push given pull:"git pull" end git to svn of directory given push:remote update of directory given pull:"git svn rebase", push:remote end svn
These two handlers make it quicker to define git and SVN (technically git-svn) based downloads. I only really maintain git mirrors of SVN repos, so all git repos skip the push step. This makes it really easy to add new repositories here...
activate application "Terminal" git at "git" git at "tig" svn of "pugs" with push svn of "parrot" with push git at "nqp-rx" git at "rakudo" svn of "heritage" given push:"silver" tell application "Terminal" to set win's selected tab to win's first tab
Each of those lines opens a new tab in Terminal and runs the commands to update a given repository. pugs and parrot are mirrored to their default locations while heritage (a stalled personal project I want to keep up-to-date against the upstream) mirrors to my server instead.
That's a lot of work, but will let me just double-click an application to update seven programs. In the long run, it takes less time. Honest.