# # 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 +++ git.ml @@ -20,6 +20,7 @@ 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 @@ 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 @@ 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 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 +++ view.ml @@ -50,7 +50,12 @@ 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 + 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 "HEAD" + 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 @@ 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 = + 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 = - 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 = 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 +++ viz_misc.ml @@ -46,6 +46,11 @@ | _ :: 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 +++ viz_misc.mli @@ -6,6 +6,7 @@ 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