Friday, August 6, 2010

Managing remote branches with git svn

git svn is a great way to work locally using git for remote svn repositories.  The basic workflow is rebase, hack, commit, hack, commit, etc, rebase, dcommit.  You'll get lots of google results by just searching for git svn.  One feature that's really nice is interactive rebase.  For example, to interactively examine the last 8 (local) commits and have the option to delete or combine commits (squash), do
git rebase -i HEAD~8

I recently needed to create a new feature branch on the remote svn, work on it, and then merge it back into the trunk.  It took a whole lot of digging to figure this out, so I thought I'd share what ended up working. I can't vouch for the optimality or safety of any of this.  I just know that it worked for me.
First of all, I made sure to check out the repository using the --stdlayout option.
git svn clone svn+ssh://user@server/svn/repos/project --stdlayout
Then, to create a new remote svn branch called "new_feature",
git svn branch -m "Branch to add in new_feature." new_feature
And check out the new branch locally
git checkout -b new_feature new_feature
Then the usual.  Hack, commit, etc.  It's not a bad idea to first do "git svn dcommit -n" to make sure your dcommits will go to the branch directory in the remote repository.
When it's finally time to merge back into the trunk (this was the hardest part to find - this post at stackoverflow was really helpful), re-checkout the trunk to a new local branch, which I called "merge".
git checkout remotes/trunk -b merge
This seems to be the key step.  Merge the feature branch with the --squash option.
git merge --squash new_feature
Then commit and dcommit.  You may want to do a dcommit -n to reassure yourself that you're committing to the trunk.
git commit -m "Merge new_feature branch"
git svn dcommit -n  (make sure commit will go to trunk)
git svn dcommit
That's it!  You may want to clean up your branches.  This is what I did.
git checkout remotes/trunk
git checkout -b localtrunk
git branch -D new_feature
git branch -D merge

Tuesday, March 2, 2010

Switch between c/c++ source and header files in Emacs

A pretty common task when editing c/c++ programs is to switch between the source and header files (i.e. file.c or file.cpp and file.h). I had a handy keyboard shortcut in vim to do this and, despite my newfound appreciation for the concept of Emacs, this was one of those things that in practice I was sorely missing. I actually sort of expected it to be built in... M-x switch-from-source-to-header or something, but I didn't find it (please let me know if it exists!). So I did what you're supposed to do with Emacs, I forged my own. It turned out to be a pretty good exercise in learning elisp. I had to figure out how to do if-then-else, a switch statement, some simple logic, how to deal with strings, how to interact with emacs, etc. For example, there's no "else" in elisp, instead you do something like this.
(if (some condition)
    (then action)
  (else action)
)
However, the actions are limited to a single statement. If you want to use more than one, you have to wrap it in a (progn ), like this.
(if (some condition)
    (then action)
  (progn
    (else action 1)
    (else action 2)
    )
)
Anyways, here's my function to switch between source and header files. I'm sure there's a better way to do it. There's a couple shortcomings here - it only knows about ".h", ".c", and ".cpp" extensions and if there is no ".c" or ".cpp" file corresponding to a header, it opens up a ".cpp" file for you (maybe you wanted ".c"...). There ought to be a way around the first problem - you could at least specify a configurable table of extension associations. The second problem may be more of a design decision problem. It works pretty well for me - I develop primarily in c++, so assuming ".cpp" is fine for me. If you want the opposite behavior, just swap ".cpp" and ".c" so they are checked in the opposite order. Of course you could ask for an input, but that goes beyond the scope of what I wanted. Another open question for me is how elisp treats local and global variables. I've purposely chosen short, unintelligible variables names here to avoid clobbering some other global variable. I think the "let" command has something to do with this, but I didn't have time to work it out.
(defun dts-switch-between-header-and-source ()
  "Switch between a c/c++ header (.h) and its corresponding source (.c/.cpp)."
  (interactive)
  ;; grab the base of the current buffer's file name
  (setq bse (file-name-sans-extension buffer-file-name))
  ;; and the extension, converted to lowercase so we can
  ;; compare it to "h", "c", "cpp", etc
  (setq ext (downcase (file-name-extension buffer-file-name)))
  ;; This is like a c/c++ switch statement, except that the
  ;; conditions can be any true/false evaluated statement
  (cond
   ;; first condition - the extension is "h"
   ((equal ext "h")
    ;; first, look for bse.c 
    (setq nfn (concat bse ".c"))
    (if (file-exists-p nfn)
        ;; if it exists, either switch to an already-open
        ;;  buffer containing that file, or open it in a new buffer
        (find-file nfn)
      ;; this is the "else" part - note that if it is more than
      ;;  one line, it needs to be wrapped in a (progn )
      (progn
        ;; look for a bse.cpp
        (setq nfn (concat bse ".cpp"))
        ;; likewise 
        (find-file nfn)
        )
      )
    )
   ;; second condition - the extension is "c" or "cpp"
   ((or (equal ext "cpp") (equal ext "c"))
    ;; look for a corresponding bse.h
    (setq nfn (concat bse ".h"))
    (find-file nfn)
    )
   )
  )
(global-set-key (kbd "C-c s") 'dts-switch-between-header-and-source)

Friday, February 26, 2010

Finally getting gnus to act like an email client

I'm liking emacs. I still miss some of the vim commands, particularly numerical prefixes (I know, in emacs you can do C-u to prefix a command, but it's just not nearly as elegant) and the "repeat last edit" command ".". But on the whole, I like how integrated emacs is, how customizable it is, and actually a few of the editing commands (one of my new favorites is C-x ; to set the comment column then M-; to add a comment at the right of a group of text). Anyways, I've switched to using gnus for my email. It took a little getting used to, but yesterday I tried going back to Mail.app and found myself kind of annoyed at some of the things I just couldn't do; particularly with respect to managing my gmail account, which all of my email goes through. On the other hand, it has taken me over a week to get this to work the way I want it to. The google search results are more often than not not all that helpful, so I thought I'd document some of what I've learned here.

1. Gnus and Gmail imap. I don't remember all of the details of getting it set up, but this part of the .gnus file has the basic set-up for getting your mail from the gmail imap server:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Using gmail IMAP for receiving mail, credentials in .authinfo
(setq gnus-select-method '(nnml ""))
(add-to-list 'gnus-secondary-select-methods '(nnimap "gmail"
                                                     (nnimap-address "imap.gmail.com")
                                                     (nnimap-server-port 993)
                                                     (nnimap-stream ssl)))
;; make gnus NOT ignore [Gmail] mailboxes
(setq gnus-ignored-newsgroups "^to\\.\\|^[0-9. ]+\\( \\|$\\)\\|^[\"]\"[#'()]")
I use my Princeton email address primarily, so to send mail through Princeton smtp I use
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Using Princeton SMTP for sending mail
(setq message-sendmail-envelope-from 'header)
(setq send-mail-function 'smtpmail-send-it
      message-send-mail-function 'smtpmail-send-it)
(setq smtpmail-default-smtp-server "smtp.princeton.edu"
      smtpmail-smtp-server "smtp.princeton.edu"
      smtpmail-smtp-service 587
      smtpmail-starttls-credentials '(("smtp.princeton.edu" 587 nil nil)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; This is the "proper" way to set up the "from" information for new mail
;;   Note I BCC to myself @ gmail so that it gets stored in my gmail
(setq gnus-posting-styles
      '((".*"
         (name "Daniel Swain")
         (address "dswain@princeton.edu")
         (BCC "dan.t.swain@gmail.com"))))
For both I use a ~/.authinfo file which should look something like this:
machine imap.gmail.com login dan.t.swain@gmail.com password mysupersecretpassword port 993
machine smtp.princeton.edu login dswain password
myothersupersecretpassword port 587
This bit gets gnus to stop asking me how many old emails I want to download. You could change 'some to some big number, but I keep my inbox pretty clean, so it's not a big deal.
;; don't ask how many emails to download
(setq gnus-large-newsgroup 'nil)
The final bit took me the longest to figure out. Every time I got new email, gnus would only show the new email in the buffer and hide all of my old emails until I read the new one and hit M-g again to rescan the inbox. A bit of C-h k to figure out what the function call was led me to this solution, in which I bind C-c C-c in the summary buffer to "Check for new mail", which is a call to the same function as M-g but with 'all as an argument.
;; tells gnus to get new mail and also display all old mail
(define-key gnus-summary-mode-map (kbd "C-c C-c")
  (lambda ()
    (interactive)
    (gnus-summary-rescan-group 'all)))

2. w3m and Aquamacs 2.0 preview 4b. When I upgraded to Aquamacs 2.0 preview 4b (which is based on Emacs23.1 rather than Emacs22.something), gnus seemed to stop wanting to display the contents of some emails, instead just displaying the headers. It turns out this was because those emails used HTML and w3m uses the w3m browser to show HTML emails. I had download w3m before and installed emacs-w3m under Aquamacs 1.9, but apparently something broke in the upgrade. I was able to fix it by downloading the CVS version of emacs-w3m (I didn't have to reinstall w3m, which I had installed using darwinports) and configuring it using --with-emacs=/Applications/Aquamacs.app/Contents/MacOS/Aquamacs. I did a "make" but not "make install", instead I copied the whole emacs-w3m folder to my emacs packages directory (~/.emacs-pkgs, but the name isn't important) and loading it in my .emacs.el with
;; Use w3m for web browsing
(setq load-path (cons "~/.emacs-pkgs/emacs-w3m/" load-path))
(require 'w3m-load)

3. I used htmlize to generate the code snippets in this post.

Tuesday, February 16, 2010

Posting from Emacs using gblogger

I've been playing with Emacs lately. The MO of Emacs is to avoid leaving it any more than absolutely necessary, so there are packages for doing all kinds of stuff. One of those things is posting to blogger. It didn't take too long to find this article introducing g-client, which can be downloaded from google here. Downloading and installing it were relatively painless, except that you need to run make config before make to avoid errors about missing g-cus-load.el. The hard part was figuring out, for someone who didn't "just know", what the "post url" was. So here's how you figure it out. Run gblogger-blogs and view the resulting page (for me it just opened in my browser) - it should be a plain text page with a handful of links. At the bottom of the page is a link "[post]". Right click and copy the address for that - that's the "post url". Having that, I of course started hacking my .emacs.el:

(defun dts-new-blog-post ()
(interactive)
(gblogger-new-entry "http://www.blogger.com/feeds/7785890353048895648/posts/default"))
and mapped it to C-c C-b.

BTW 1 - It was super easy to copy and paste the code from my .emacs.el, since it was open in another buffer.

BTW 2 - In writing this I found the following two commands really useful:

w3m-print-current-url
Prints the url of the current page in the minibuffer, but, possibly more useful, copies it to the kill-ring.
w3m-print-this-url
Same as above, but for the currently hilighted link.

BTW 3 - When trying to post this it complained that I had an HTML error and I freaked a little about getting my post back, but all I had to do was undo a couple times (C-x u).

BTW 4 - I went back and edited this post to remove some extra blank lines. Unlike normal HTML, they seem to matter here and were producing MEGA spacing.

Test post from Emacs



This is a test post from Emacs.

Tuesday, January 12, 2010

Random Update - New Camera Gear

So I saved up some birthday and Christmas money and got a couple new items of camera gear. I got the Nikkor 50 mm f/1.8, which for less than $150 is really a no-brainer. It's super sharp and fast (there's a reason they call it the "nifty fifty"). I like it a lot. I also picked up Manfrotto 190XB tripod legs and a 484rc2 quick release head. It's not the biggest tripod but that was kind of the point - I want to be able to tote it around hiking or throw it in luggage for traveling. I haven't had any problems at all with stability or supporting my gear.

I took this shot using the tripod at Core Creek County Park here in Newtown. I was driving home and noticed the incredible sunset happening but didn't have my gear so I rushed into the apartment, grabbed everything, and flew back to the east side of the lake. I think I was a little past the best light but I think the shot came out pretty well. I used my 18-55 at 18mm, f/3.5 for 1/2 second. In retrospect I think I could have stopped down a little for more sharpness and depth, but not bad for a first try. I didn't have to do much in post, I think I lightened the lake and darkened the sky slightly and darkened the blacks in the middle. The lake was partially frozen.