(* commit.ml - Don Yang (uguu.org) 04/16/06 *) (* Log entries to changelog *) let append_to_changelog repository_root version added changed deleted count = let file_list = List.sort compare (List.rev_append (List.rev_append (List.map (fun x -> "\t" ^ x ^ " (add)\n") added) (List.map (fun x -> "\t" ^ x ^ " (edit)\n") changed)) (List.map (fun x -> "\t" ^ x ^ " (delete)\n") deleted)) in let log_file = Unix.openfile (repository_root ^ Path.separator_str ^ Repository.changelog) [Unix.O_CREAT; Unix.O_WRONLY; Unix.O_APPEND] 0o644 in if (Unix.lseek log_file 0 Unix.SEEK_END) < 0 then failwith "IO error"; let h_len = (String.length version) + 1 in if (Unix.single_write log_file (version ^ "\n") 0 h_len) < h_len then failwith "IO error"; List.iter (fun x -> if (Unix.single_write log_file x 0 (String.length x)) < (String.length x) then failwith "IO error") file_list; Unix.close log_file; Printf.printf "Added %d files to %s%c%s\n" count repository_root Path.separator version; Printf.printf "\t%d new, %d changed, %d deleted\n" (List.length added) (List.length changed) (List.length deleted);; (* Convert list of files and directories to hashtable of file to attributes *) let insert_file_stat_pair dict file_stat_pair = if (snd file_stat_pair).Unix.st_kind = Unix.S_REG then Hashtbl.add dict (fst file_stat_pair) (snd file_stat_pair);; let file_list_to_attr_map file_list = let h = Hashtbl.create (List.length file_list) in List.iter (insert_file_stat_pair h) file_list; h;; (* Create first repository snapshot *) let commit_first_snapshot () = let repository_root = Repository.create() in if repository_root = "" then failwith "Can not create repository"; let version = Version.new_version() in let target_root = repository_root ^ Path.separator_str ^ version in Unix.mkdir target_root 0o750; let source_root = Repository.current_root() in if source_root = "" then failwith "Can not enumerate source"; let (source_dirs, source_files) = Dir.split_file_list (Dir.enumerate source_root) in Dir.create_forest (List.map (Path.replace_root source_root target_root) source_dirs); List.iter (fun x -> File.copy_with_stat x (Path.replace_root source_root target_root (fst x))) source_files; append_to_changelog repository_root version (List.map (fun x -> Path.replace_root source_root "" (fst x)) source_files) [] [] (List.length source_files);; (* Find added and changed files in two sets of files *) let rec forward_compare old_root old_file_map new_root new_files added changed = match new_files with a::b -> (let new_file_name = fst a in try let old_stat = Hashtbl.find old_file_map new_file_name in if (File.compare_with_stat ((old_root ^ new_file_name), old_stat) ((new_root ^ new_file_name), snd a)) then (forward_compare old_root old_file_map new_root b added (new_file_name::changed)) else (forward_compare old_root old_file_map new_root b added changed) with Not_found -> forward_compare old_root old_file_map new_root b (new_file_name::added) changed) | [] -> (added, changed);; (* Find deleted files *) let rec backward_compare new_file_map old_files deleted = match old_files with a::b -> (try Hashtbl.find new_file_map (fst a); backward_compare new_file_map b deleted with Not_found -> backward_compare new_file_map b ((fst a)::deleted)) | [] -> deleted;; (* Create repository snapshot, comparing each file with the last version *) let commit_with_diff last_snapshot = let repository_root = Repository.locate() in if repository_root = "" then failwith "Can not find repository"; let version = Version.new_version() in let target_root = repository_root ^ Path.separator_str ^ version in Unix.mkdir target_root 0o750; let source_root = Repository.current_root() in if source_root = "" then failwith "Can not enumerate source"; let (source_dirs, source_files) = Dir.split_file_list (Dir.enumerate source_root) in let (last_dirs, last_files) = Dir.split_file_list (Dir.enumerate last_snapshot) in let old_files = Dir.replace_root last_snapshot "" last_files in let new_files = Dir.replace_root source_root "" source_files in let old_file_map = file_list_to_attr_map old_files in let new_file_map = file_list_to_attr_map new_files in let (added, changed) = forward_compare last_snapshot old_file_map source_root new_files [] [] in let deleted = backward_compare new_file_map old_files [] in Dir.create_forest (List.map (Path.replace_root source_root target_root) source_dirs); List.iter (fun x -> File.copy_with_stat x (Path.replace_root source_root target_root (fst x))) source_files; append_to_changelog repository_root version added changed deleted (List.length source_files);; (* Create new snapshot in repository *) let commit () = let last_root = Version.last_snapshot_path() in if last_root = "" then (commit_first_snapshot()) else commit_with_diff last_root;;