The unified diff between revisions [84b77f76..] and [6e1b1069..] is displayed below. It can also be downloaded as a raw diff.

#
#
# patch "git.ml"
#  from [1a02999ff418b308b65af1f90f0cc4776339a298]
#    to [c5709e028ce1670f8b42fab662bc3204f3d6590a]
#
# patch "view.ml"
#  from [feb6b7faf666d8dc16506d203149f970f08140d8]
#    to [85fe039f1aaf23419b2c230887c361382493c78a]
#
# patch "viz_misc.ml"
#  from [1462c7cfe31639613d5cbef63e79a72f3c0cceb3]
#    to [c0f7e882e80792536f5ec1e63695211c306a66e6]
#
# patch "viz_misc.mli"
#  from [f98871b0abb3298c963a0961f30c113219b6626e]
#    to [3a6714c41817a7f1c71d6852e4cc45b7c4962871]
#
============================================================
--- git.ml	1a02999ff418b308b65af1f90f0cc4776339a298
+++ git.ml	c5709e028ce1670f8b42fab662bc3204f3d6590a
@@ -20,6 +20,7 @@ type t = {
     get_commit    : (string -> commit) ;
     get_changeset : (string -> changeset) ;
     tags          : (string * string) list ;
+    branches      : (string * string) list ;
   }

 let id_of_file ?dir f =
@@ -27,14 +28,17 @@ let id_of_file ?dir f =
     input_line
     (match dir with None -> f | Some d -> Filename.concat d f)

-let fetch_tags git_dir =
+let fetch_dir_of_ids git_dir subdir =
   try
-    let tags_dir = Filename.concat git_dir "tags" in
+    let dir = Filename.concat git_dir subdir in
     List.map
-      (fun t -> (id_of_file ~dir:tags_dir t, t))
-      (Array.to_list (Sys.readdir tags_dir))
+      (fun n -> (id_of_file ~dir n, n))
+      (Array.to_list (Sys.readdir dir))
   with _ -> []

+let fetch_tags git_dir =
+  fetch_dir_of_ids git_dir "tags"
+
 let fetch_history base id =
   log "exec" "### exec: Running rev-tree %s'" id ;
   match Gspawn.sync
@@ -211,7 +215,9 @@ let open_db db_name =
       head = head ;
       get_commit = get_commit ;
       get_changeset = get_changeset ;
-      tags = fetch_tags d }
+      tags = fetch_tags d ;
+      branches = fetch_dir_of_ids d "heads"
+    }
   with Failure _ | Sys_error _ ->
     Viz_types.errorf "Not a git db: %s" db_name

@@ -219,10 +225,20 @@ let get_filename d = d.base

 let get_filename d = d.base

-let fetch_branches d = []
+let fetch_branches d =
+  List.sort compare
+    (List.map snd d.branches)

-let fetch_ancestry_graph d _ =
-  scan_history (fetch_history d.base d.head)
+let fetch_ancestry_graph d q =
+  let head =
+    match q with
+    | BRANCH b ->
+	begin
+	  try list_rassoc b d.branches
+	  with Not_found -> d.head
+	end
+    | _ -> d.head in
+  scan_history (fetch_history d.base head)

 let fetch_revision d id =
   { revision_id  = id ;
============================================================
--- view.ml	feb6b7faf666d8dc16506d203149f970f08140d8
+++ view.ml	85fe039f1aaf23419b2c230887c361382493c78a
@@ -50,7 +50,12 @@ type info_display = {
     cert_view       : GTree.view ;
   }

-type branch_selector = unit
+type branch_selector = {
+    combo : GEdit.combo_box GEdit.text_combo ;
+    mutable combo_signal : GtkSignal.id option ;
+    mutable branches : string array ;
+    select_signal : Viz_types.query Signal.t ;
+  }

 type event = [
     `CLEAR
@@ -316,14 +321,96 @@ module Branch_selector = struct

 
 module Branch_selector = struct
+  let select_branch s =
+    let (combo, _) = s.combo in
+    try
+      Signal.emit s.select_signal
+	begin
+	  match combo#active with
+	  | -1 -> raise Exit
+	  | 0 -> ALL
+	  | i -> BRANCH s.branches.(i - 1)
+	end
+    with Exit -> ()
+
+  let with_inactive_combo ({ combo = (combo, _) } as s) f =
+    let id = some s.combo_signal in
+    GtkSignal.handler_block combo#as_widget id ;
+    f s.combo ;
+    GtkSignal.handler_unblock combo#as_widget id
+
   let make ~packing =
     let hb = GPack.hbox ~border_width:4 ~packing () in
+    let combo =
+      ignore (GMisc.label ~text:"Branch: " ~packing:hb#pack ()) ;
+      let (model, column) as store = GTree.store_of_list Gobject.Data.string [] in
+      let combo = GEdit.combo_box ~model ~packing:hb#pack () in
+      let r = GTree.cell_renderer_text [] in
+      combo#pack r ;
+      combo#add_attribute r "markup" column ;
+      (combo, store) in
     let entry = GEdit.entry ~packing:(hb#pack ~from:`END) () in
+    begin
+      let tooltips = GData.tooltips () in
+      tooltips#set_tip
+	~text:"Find a node by its revision id or tag"
+	entry#coerce
+    end ;
     let lbl = GMisc.label ~text:"Find:" ~packing:(hb#pack ~from:`END) () in
+    let c =
+      { combo = combo ; combo_signal = None ;
+	branches = [||] ;
+	select_signal = Signal.make () } in
+    begin
+      let callback () = select_branch c in
+      let (combo, _) = combo in
+      c.combo_signal <- Some (combo#connect#changed ~callback)
+    end ;
     let f = { last_find = "", [] ; find_signal = Signal.make () ; find_entry = entry } in
     ignore (entry#connect#activate (fun () ->
       Signal.emit f.find_signal entry#text)) ;
-    ((), f)
+    (c, f)
+
+  let connect v f =
+    Signal.connect v.selector.select_signal f
+
+  let clear { selector = s } =
+    s.branches <- [||] ;
+    with_inactive_combo s
+      (fun (_, (model, _)) -> model#clear ())
+
+  let populate { selector = s } br =
+    with_inactive_combo s
+      (fun (combo, (model, column)) ->
+	s.branches <- Array.of_list br ;
+	begin
+	  let row = model#append () in
+	  model#set ~row ~column "<i>HEAD</i>"
+	end ;
+	List.iter
+	  (fun b ->
+	    let row = model#append () in
+	    model#set ~row ~column (Glib.Markup.escape_text (utf8ize b)))
+	  br)
+
+  let set_branch { selector = s } b =
+    let (combo, _) = s.combo in
+    combo#set_active
+      begin
+	match b with
+	| None -> 0
+	| Some b ->
+	    try 1 + array_index s.branches b
+	    with Not_found ->
+	      error_notice_f ~parent:(fst s.combo) "Could not find the branch '%s'" b ;
+	      -1
+      end
+
+  let get_branch { selector = s } =
+    let (combo, _) = s.combo in
+    match combo#active with
+    | i when i > 0 -> Some s.branches.(i - 1)
+    | _ -> None
 end


@@ -873,6 +960,8 @@ let make ~aa ~prefs ~packing =
     status_reporter = lazy (Status.new_reporter "monotone")
   } in

+  Branch_selector.connect v (handle_query v) ;
+
   connect_event v (function
     | `NODE_SELECT id ->
 	Canvas.display_selection_marker v id ;
@@ -890,6 +979,7 @@ let close v =


 let close v =
+  Branch_selector.clear v ;
   may Agraph.abort_layout v.agraph ;
   Canvas.clear v ;
   v.agraph <- None ;
@@ -903,12 +993,12 @@ let open_db v fname branch =


 let open_db v fname branch =
-  assert (branch = None) ;
   close v ;
   try
     let db = Database.open_db fname in
     v.db <- Some db ;
-    handle_query v ALL ;
+    Branch_selector.populate v (Database.fetch_branches db) ;
+    Branch_selector.set_branch v branch ;
     Signal.emit v.event_signal `OPEN_DB
   with Viz_types.Error msg ->
     error_notice ~parent:v.canvas.w msg
@@ -916,8 +1006,9 @@ let reload v =

 let reload v =
   may (fun db ->
+    let branch = Branch_selector.get_branch v in
     let fname = Database.get_filename db in
-    open_db v fname None)
+    open_db v fname branch)
     v.db


============================================================
--- viz_misc.ml	1462c7cfe31639613d5cbef63e79a72f3c0cceb3
+++ viz_misc.ml	c0f7e882e80792536f5ec1e63695211c306a66e6
@@ -46,6 +46,11 @@ let rec list_assoc_all k = function
   | _ :: tl -> list_assoc_all k tl
   | [] -> []

+let rec list_rassoc v = function
+  | (a, b) :: _ when b = v -> a
+  | _ :: tl -> list_rassoc v tl
+  | [] -> raise Not_found
+
 let array_index a v =
   let rec loop i =
     if i >= Array.length a
============================================================
--- viz_misc.mli	f98871b0abb3298c963a0961f30c113219b6626e
+++ viz_misc.mli	3a6714c41817a7f1c71d6852e4cc45b7c4962871
@@ -6,6 +6,7 @@ val list_assoc_all : 'a -> ('a * 'b) lis
 val option_of_list : 'a list -> 'a option
 val list_uniq : 'a list -> 'a list
 val list_assoc_all : 'a -> ('a * 'b) list -> 'b list
+val list_rassoc : 'b -> ('a * 'b) list -> 'a (** @raise Not_found *)
 val array_index : 'a array -> 'a -> int (** @raise Not_found *)

 val some : 'a option -> 'a