bool RemoteRepositoryAssistant::try_git_checkout_repository (const ustring& local, const ustring& remote)
// Checks the remote repository out into the local one.
{
  // Directory to run the cloning process in.
  ustring cloning_directory = git_testing_directory (local) + "clone";
  gw_mkdir_with_parents(cloning_directory);

  // Clone the remote repository.
  GwSpawn spawn("git");
  spawn.arg("clone");
  spawn.workingdirectory(cloning_directory);
  spawn.arg(git_testing_directory (remote));
  spawn.run();
  bool okay = (spawn.exitstatus == 0);

  // Message if things didn't work out.
  if (!okay) {
    gtk_label_set_text(GTK_LABEL(label_try_git), _("git clone fails to clone the repository"));
  }
  
  // Move the repository into place.
  ustring cloned_directory = gw_build_filename (cloning_directory, remote);
  ustring local_directory = git_testing_directory (local);
  unix_rmdir(local_directory);
  if (okay) {
    unix_mv(cloned_directory, local_directory);
  }
  unix_rmdir(cloning_directory);
  
  // Return result.
  return okay;
}
void RemoteRepositoryAssistant::repository_unclone ()
// Does the house keeping for indicating that the repository was not cloned.
{
  unix_rmdir(persistent_clone_directory);
  previously_cloned_url.clear();
  write_access_granted = false;
}
void RemoteRepositoryAssistant::on_button_clone ()
{
  // Progress.
  ProgressWindow progresswindow (_("Cloning repository"), false);
  progresswindow.set_fraction (0.5);
  
  // Clear out persistent clone directory.
  repository_unclone();
  
  // Create temporal clone directory.
  ustring temporal_clone_directory = git_testing_directory ("tempclone");
  unix_rmdir(temporal_clone_directory);
  gw_mkdir_with_parents(temporal_clone_directory);
  
  // Clone the remote repository
  GwSpawn spawn("git");
  spawn.workingdirectory(temporal_clone_directory);
  spawn.arg ("clone");
  spawn.arg(repository_url_get());
  spawn.run();

  if (spawn.exitstatus == 0) {
    // Move the cloned repository into the persistent clone directory.
    ReadDirectories rd (temporal_clone_directory, "", "");
    if (!rd.directories.empty()) {
      ustring subdirectory = rd.directories[0];
      subdirectory = gw_build_filename (temporal_clone_directory, subdirectory);
      unix_mv (subdirectory, persistent_clone_directory);
    }
    unix_rmdir(temporal_clone_directory);
  } else {
    // Clone failed, clear out any remains.
    repository_unclone();
  }  
  
  // Update structures.
  gtk_assistant_set_page_complete (GTK_ASSISTANT (assistant), vbox_clone, repository_was_cloned());
  if (repository_was_cloned()) {
    gtk_label_set_text (GTK_LABEL (label_clone), _("The data has been cloned, you can go forward"));
    previously_cloned_url = repository_url_get();
  } else {
    gtk_label_set_text (GTK_LABEL (label_clone), _("Cloning the data failed, please try again"));
    repository_unclone();
  }
}
示例#4
0
void XeTeX::place_ptx2pdf_macros ()
{
  GwSpawn spawn (Directories->get_tar());
  spawn.workingdirectory (working_directory);
  spawn.arg ("zxf");
  spawn.arg (gw_build_filename (Directories->get_package_data (), "ptx2pdf.tar.gz"));
  spawn.run ();
  ustring ptx2pdf_directory = "ptx2pdf";
  ReadFiles rf (gw_build_filename (working_directory, ptx2pdf_directory), "", "");
  for (unsigned int i = 0; i < rf.files.size(); i++) {
    unix_mv (gw_build_filename (working_directory, ptx2pdf_directory, rf.files[i]), gw_build_filename (working_directory, rf.files[i]));
  }
  unix_rmdir (gw_build_filename (working_directory, ptx2pdf_directory));
}
void RemoteRepositoryAssistant::on_assistant_apply ()
{
  // Configurations.
  extern Settings *settings;
  ProjectConfiguration *projectconfig = settings->projectconfig(bible);

  // Whether to use the remote repository.
  bool use_remote_repository = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton_use_repository));
  if (bible_notes_selector_bible ())
    projectconfig->git_use_remote_repository_set(use_remote_repository);
  else
    settings->genconfig.consultation_notes_git_use_remote_repository_set(use_remote_repository);

  // The remote repository URL.
  if (bible_notes_selector_bible ())
    projectconfig->git_remote_repository_url_set(repository_url_get());
  else
    settings->genconfig.consultation_notes_git_remote_repository_url_set(repository_url_get());
  
  // If the repository was cloned, move it into place.
  if (repository_was_cloned()) {
    ustring destination_data_directory;
    if (bible_notes_selector_bible ())
      destination_data_directory = project_data_directory_project(bible);
    else
      destination_data_directory = notes_shared_storage_folder ();
    unix_rmdir(destination_data_directory);
    unix_mv(persistent_clone_directory, destination_data_directory);
    // Switch rename detection off. 
    // This is necessary for the consultation notes, since git has been seen to cause spurious renames.
    GwSpawn spawn ("git");
    spawn.workingdirectory (destination_data_directory);
    spawn.arg ("config");
    spawn.arg ("--global");
    spawn.arg ("diff.renamelimit");
    spawn.arg ("0");
    spawn.run ();
  }

  if (bible_notes_selector_bible ()) {
    // Take a snapshot of the whole project.
    snapshots_shoot_project (bible);
  } else{
    // Create the index for the consultation notes.
    notes_create_index ();
  }

  // Show summary.
  gtk_assistant_set_current_page (GTK_ASSISTANT (assistant), summary_page_number);
}
示例#6
0
void git_upgrade ()
// Upgrades the git system.
{
  // Go through the projects that have their git repository enabled.
  extern Settings * settings;
  vector <ustring> projects = projects_get_all ();
  for (unsigned int i = 0; i < projects.size(); i++) {
    ProjectConfiguration * projectconfig = settings->projectconfig (projects[i]);
    ustring git_directory = gw_build_filename (project_data_directory_project (projects[i]), ".git");
    if (projectconfig->git_use_remote_repository_get()) {
      // At times there's a stale index.lock file that prevents any collaboration.
      // This is to be removed.
      ustring index_lock = gw_build_filename (git_directory, "index.lock");
      if (g_file_test (index_lock.c_str(), G_FILE_TEST_IS_REGULAR)) {
        gw_message (_("Cleaning out index lock ") + index_lock);
        unix_unlink (index_lock.c_str());
      }
      // Get the data directory for the project
      ustring datadirectory = tiny_project_data_directory_project(projects[i]);
      // On most machines git can determine the user's name from the system services. 
      // On the XO machine, it can't. It is set here manually. 
      // On more recent versions of git, like version 1.8.3 and younger,
      // although git may determine the user's name from the system, 
      // it still requires it to be set manually.
      ustring command;
      command = "git config user.email \"";
      command.append(g_get_user_name());
      command.append("@");
      command.append(g_get_host_name());
      command.append("\"");
      maintenance_register_shell_command (datadirectory, command);
      command = "git config user.name \"";
      command.append(g_get_real_name());
      command.append("\"");
      maintenance_register_shell_command (datadirectory, command);
      // (Re)initialize the repository. This can be done repeatedly without harm.
      // Note that this is done on shutdown.
      maintenance_register_shell_command (datadirectory, "git init");
    } else {
      if (g_file_test (git_directory.c_str(), G_FILE_TEST_IS_DIR)) {
        gw_message (_("Cleaning out folder ") + git_directory);
        ProgressWindow progresswindow (_("Tidying up project ") + projects[i], false);
        progresswindow.set_fraction (0.5);
        unix_rmdir (git_directory);
      }
    }
  }
}
示例#7
0
void OpenDocument::unpack_template()
{
  // Clear working directory.
  unix_rmdir(workingdirectory);
  gw_mkdir_with_parents(workingdirectory);
  // Copy template there.
  // Note: To create the template use zip -r template.odt *
#ifdef WIN32
  ustring command = "unzip -o ";
  command.append(gw_build_filename(Directories->get_package_data(), "template.odt"));
  command.append(" -d ");
  command.append(shell_quote_space(workingdirectory));
#else
  ustring command = "cd";
  command.append(shell_quote_space(workingdirectory));
  command.append("; cp ");
  command.append(gw_build_filename(Directories->get_package_data(), "template.odt"));
  command.append(" .; unzip *; rm *.odt");
#endif
  if (system(command.c_str())) ; // This one does not work with GwSpawn because of the wildcards used.
}
void RestoreAssistant::on_assistant_apply ()
{
  // Unpack the tarball.
  ustring unpack_directory = gw_build_filename (Directories->get_temp (), "restore");
  unix_rmdir (unpack_directory);
  gw_mkdir_with_parents (unpack_directory);
  if (uncompress (filename, unpack_directory)) {

    // Do specialized operations on the unpacked data.
    switch (get_type()) {
      case btBible:
      {
        restore_project (unpack_directory, bible_name, restore_feedback);
        break;
      }
      case btNotes:
      {
        restore_notes (unpack_directory, restore_feedback);
        break;
      }
      case btResource:
      {
        restore_resource (unpack_directory, restore_feedback);
        break;
      }
      case btAll:
      {
        restore_all_stage_one (unpack_directory, restore_feedback);
        break;
      }
    }
  } else {
    restore_feedback.push_back (_("Failed to unpack file ") + filename);
  }
 
  // Show summary.
  gtk_assistant_set_current_page (GTK_ASSISTANT (assistant), page_number_progress);
}
示例#9
0
void XeTeX::create_work_area ()
{
  working_directory = gw_build_filename (Directories->get_temp (), "xetex");
  unix_rmdir (working_directory);
  gw_mkdir_with_parents (working_directory);
}
示例#10
0
void mechon_mamre_action_page (HtmlWriter2& htmlwriter)
{
  htmlwriter.heading_open (3);
  htmlwriter.text_add (_("Hebrew import from Mechon Mamre"));
  htmlwriter.heading_close ();

  vector <ustring> messages;
  bool keep_going = true;

  // Locate the downloaded file.
  ustring ct005zipfilename = gw_build_filename (g_get_home_dir (), "ct005.zip");
  messages.push_back (_("Looking for file ") + ct005zipfilename);
  if (!g_file_test (ct005zipfilename.c_str(), G_FILE_TEST_IS_REGULAR)) {
    ct005zipfilename.clear();
  }
  if (ct005zipfilename.empty()) {
    ct005zipfilename = gw_build_filename (g_get_home_dir (), "Desktop", "ct005.zip");
    messages.push_back (_("Looking for file ") + ct005zipfilename);
    if (!g_file_test (ct005zipfilename.c_str(), G_FILE_TEST_IS_REGULAR)) {
      ct005zipfilename.clear();
    }
  }
  if (ct005zipfilename.empty()) {
    ct005zipfilename = gw_build_filename (g_get_home_dir (), "Downloads", "ct005.zip");
    messages.push_back (_("Looking for file ") + ct005zipfilename);
    if (!g_file_test (ct005zipfilename.c_str(), G_FILE_TEST_IS_REGULAR)) {
      ct005zipfilename.clear();
    }
  }
  if (ct005zipfilename.empty()) {
    messages.push_back (_("Can't find Hebrew input file"));
    keep_going = false;
  }
  if (keep_going) {
    messages.push_back (_("Using file ") + ct005zipfilename);
  }

  // Unpack the zipped file.
  ustring directory;
  if (keep_going) {
    directory = gw_build_filename (Directories->get_temp (), "uncompress");
    unix_rmdir (directory);
    gw_mkdir_with_parents (directory);
    if (!uncompress (ct005zipfilename, directory)) {
      messages.push_back (_("Could not unpack the file"));
      keep_going = false;
    }
    messages.push_back (_("Unpacking into folder ") + directory);
  }

  // Show the readme file.
  if (keep_going) {
    ustring readmefile = gw_build_filename (directory, "readme.txt");
    ReadText rt (readmefile, true, true);
    ustring line;
    for (unsigned int i = 0; i < rt.lines.size(); i++) {
      if (rt.lines[i].empty()) {
        if (!line.empty()) {
          messages.push_back (line);
          line.clear();
        }
      } else {
        line.append (rt.lines[i] + " ");
      }
    }
    messages.push_back (line);
  }

  // Look for the directory where all the html files reside.
  if (keep_going) {
    directory = gw_build_filename (directory, "c", "ct");
    if (!g_file_test (directory.c_str(), G_FILE_TEST_IS_DIR)) {
      messages.push_back (_("Can't find data in directory ") + directory);
    }
    messages.push_back (_("Looking for data in directory ") + directory);
  }
  
  // Get a list of the html files that have the data.
  vector <ustring> files;
  if (keep_going) {
    ReadFiles rf (directory, "c", ".htm");
    for (unsigned int i = 0; i < rf.files.size(); i++) {
      ustring filename = gw_build_filename (directory, rf.files[i]);
      // Check on a few characteristics.
      if (mechon_mamre_copyright(filename)) {
        unsigned int digitcount = digit_count_in_string(rf.files[i]);
        if ((digitcount == 3) || (digitcount == 4)) {
          files.push_back(filename);
        }
      }
    }
  }

  // Create a new Bible into which to import the data.
  ustring bible = _("Hebrew Mechon Mamre");
  if (keep_going) {
    if (project_exists (bible)) {
      messages.push_back (_("A Bible already exists by this name: ") + bible);
      keep_going = false;      
    } else {
      project_create_restore (bible, "");
      messages.push_back (_("Creating a new Bible called \"") + bible + "\"");
      // Make a couple of settings.
      extern Settings * settings;
      ProjectConfiguration * projectconfig = settings->projectconfig (bible);
      projectconfig->versification_set ("Original");
      projectconfig->editable_set (false);
      projectconfig->right_to_left_set (true);
      projectconfig->spelling_check_set (false);
    }
  }

  // Store all the chapters 0 in each book.
  if (keep_going) {
    vector <unsigned int> books = books_type_to_ids(btOldTestament);
    ProgressWindow progresswindow (_("Creating books"), false);
    progresswindow.set_iterate (0, 1, books.size());
    for (unsigned int bk = 0; bk < books.size(); bk++) {
      progresswindow.iterate ();
      vector <ustring> usfm;
      usfm.push_back ("\\id " + books_id_to_paratext (books[bk]));
      CategorizeChapterVerse ccv (usfm);      
      project_store_chapter (bible, books[bk], ccv);
    }  
  }

  // Store all the chapters.
  if (keep_going) {
    ProgressWindow progresswindow (_("Importing chapters"), false);
    progresswindow.set_iterate (0, 1, files.size());
    for (unsigned int i = 0; i < files.size(); i++) {
      progresswindow.iterate ();
      unsigned int book = 0;
      unsigned int chapter = 0;
      mechon_mamre_extract_book_chapter (files[i], book, chapter);
      vector <ustring> contents = mechon_mamre_extract_contents (files[i], chapter);
      CategorizeChapterVerse ccv (contents);
      project_store_chapter (bible, book, ccv);
      messages.push_back (_("Importing ") + books_id_to_localname (book) + " " + convert_to_string (chapter) + _(" from file ") + files[i]);
    }
  }

  // Write accumulated messages.
  htmlwriter.heading_open (3);
  if (keep_going) {
    htmlwriter.text_add (_("Success! Bible was created: ") + bible);
  } else {
    htmlwriter.text_add (_("Error!"));
  }
  htmlwriter.heading_close ();
  if (keep_going) {
    htmlwriter.paragraph_open ();
    htmlwriter.text_add (_("To view the Hebrew text, open the Bible in the editor. Optionally set the font for better display of the Hebrew text. A donation made to Mechon Mamre will support their work."));
    htmlwriter.paragraph_close ();
  }
  for (unsigned int i = 0; i < messages.size(); i++) {
    htmlwriter.paragraph_open ();
    htmlwriter.text_add (messages[i]);
    htmlwriter.paragraph_close ();
  }  
  
  // Write OK.
  htmlwriter.paragraph_open ();
  htmlwriter.hyperlink_add ("ok", _("Ok"));
  htmlwriter.paragraph_close ();
}
示例#11
0
void ImportAssistant::on_button_files ()
// Selection and processing of the files to import.
{
  // Set directory.
  ustring directory;
  if (!files_names.empty()) {
    directory = gw_path_get_dirname (files_names[0]);
  }

  // Processing variables.
  files_messages.clear();
  files_book_ids.clear();
  
  // Select files.
  {
    vector <ustring> files = gtkw_file_chooser_open_multiple (assistant, "", directory);
    if (!files.empty()) {
      files_names = files;
    }
  }

  // Ensure that there are only uncompressed files, or only one compressed file.
  vector <ustring> compressed_files;
  for (unsigned int i = 0; i < files_names.size(); i++) {
    if (compressed_archive_recognized (files_names[i]))
      compressed_files.push_back (files_names[i]);
  }
  if (!compressed_files.empty()) {
    if (compressed_files.size() > 1) {
      files_messages.push_back (_("You have selected more than one compressed file"));
    }
    if (compressed_files.size() == 1) {
      if (files_names.size() != 1) {
        files_messages.push_back (_("You have selected a mixture of normal and compressed files"));
      }
    }
  }

  // Optionally uncompress the archive and let the user select files from within it.
  if (files_messages.empty()) {
    if (compressed_files.size() == 1) {
      ustring unpack_directory = gw_build_filename (Directories->get_temp (), "uncompress");
      unix_rmdir (unpack_directory);
      uncompress (compressed_files[0], unpack_directory);
      gtkw_dialog_info (assistant, _("You will now be asked to select files from within the compressed archive"));
      files_names = gtkw_file_chooser_open_multiple (assistant, "", unpack_directory);
    }
  }

  // Check that all files are in Unicode.
  if (files_messages.empty()) {
    vector <ustring> unicode_files;
    vector <ustring> non_unicode_files;
    for (unsigned int i = 0; i < files_names.size(); i++) {
      ustring contents;
      gchar *s;
      g_file_get_contents(files_names[i].c_str(), &s, NULL, NULL);
      contents = s;
      g_free(s);
      if (contents.validate()) {
        unicode_files.push_back (files_names[i]);
      } else {
        non_unicode_files.push_back (files_names[i]);
      }
    }
    files_names = unicode_files;
    if (!non_unicode_files.empty()) {
      files_messages.push_back (_("The following files are not in the right Unicode format and are therefore not fit for import:"));
      for (unsigned int i = 0; i < non_unicode_files.size(); i++) {
        files_messages.push_back (non_unicode_files[i]);
      }
      files_messages.push_back (_("The online help provides more information about how to make these fit for use."));
    }
  }

  // Check that at least one file was selected.
  if (files_messages.empty()) {
    if (files_names.empty()) {
      files_messages.push_back (_("No files were selected"));
    }
  }

  // Specific checks for each import type. 
  if (files_messages.empty()) {
    switch (get_type ()) {
    case itBible:
      {
        switch (get_bible_type()) {
        case ibtUsfm:
          {
            import_check_usfm_files (files_names, files_book_ids, bible_name, files_messages);
            break;
          }
        case ibtBibleWorks:
          {
            import_check_bibleworks_file (files_names, files_book_ids, bible_name, files_messages);
            break;
          }
        case ibtOnlineBible:
          {
            break;
          }
        case ibtRawText:
          {
            break;
          }
        }
        break;
      }
    case itReferences:
      {
        break;
      }
    case itStylesheet:
      {
        break;
      }
    case itNotes:
      {
        break;
      }
    case itKeyterms:
      {
        break;
      }
    }
  }

  // Gui update.
  on_assistant_prepare (vbox_files);
}
示例#12
0
void WindowMerge::merge_edited_into_master(bool approve)
// This merges the edited data into the master data, and does error checking.
{
  // Bail out if there's nothing to merge.
  if (main_project_data == edited_project_data) {
    gtkw_dialog_info(NULL, _("Both the chapters already are the same"));
    return;
  }

  // Get the available snapshots of the master and edited projects.
  vector <unsigned int> masterseconds = snapshots_get_seconds (current_master_project, book, chapter);
  vector <unsigned int> editedseconds = snapshots_get_seconds (current_edited_project, book, chapter);

  // We need to look for the common ancestor.
  // It needs a fast routine that goes through the history as little as possible.

  // Make a combined set of the times and flags.
  vector <bool> combinedflags;
  vector <unsigned int> combinedseconds;
  for (unsigned int i = 0; i < masterseconds.size(); i++) {
    combinedflags.push_back (true);
    combinedseconds.push_back (masterseconds[i]);    
  }
  for (unsigned int i = 0; i < editedseconds.size(); i++) {
    combinedflags.push_back (false);
    combinedseconds.push_back (editedseconds[i]);    
  }
  // Sort the combined set on the time, most recent ones first.
  quick_sort (combinedseconds, combinedflags, 0, combinedseconds.size());
  {
    vector <bool> flags = combinedflags;
    vector <unsigned int> seconds = combinedseconds;
    combinedflags.clear();
    combinedseconds.clear();
    for (int i = flags.size() - 1; i >= 0; i--) {
      combinedflags.push_back (flags[i]);
      combinedseconds.push_back (seconds[i]);
    }
  }

  // Go through the history of both projects, extract the state in history,
  // and compare them in order to find the common ancestor.
  vector <ustring> mastertexts;
  vector <ustring> editedtexts;
  ustring common_ancestor;
  for (unsigned int i = 0; i < combinedseconds.size(); i++) {
    unsigned int second = combinedseconds[i];
    bool master = combinedflags[i];
    if (master) {
      mastertexts.push_back(snapshots_get_chapter(current_master_project, book, chapter, second));
    } else {
      editedtexts.push_back(snapshots_get_chapter(current_edited_project, book, chapter, second));
    }
    for (unsigned int m = 0; m < mastertexts.size(); m++) {
      for (unsigned int e = 0; e < editedtexts.size(); e++) {
        if (common_ancestor.empty()) {
          if (mastertexts[m] == editedtexts[e]) {
            common_ancestor = mastertexts[m];
          }
        }
      }
    }
    if (!common_ancestor.empty()) {
      break;
    }
  }  

  // If no common ancestor was found, give message and bail out.
  if (common_ancestor.empty()) {
    gtkw_dialog_error(NULL, _("Can't merge because a common ancestor was not found"));
    return;
  }
  // Do the merge in a temporal directory.
  workingdirectory = gw_build_filename(Directories->get_temp(), "merge");
  unix_rmdir(workingdirectory);
  gw_mkdir_with_parents(workingdirectory);

  /*
     Merge works with file1, file2 and file3.

     merge [ options ] file1 file2 file3

     merge incorporates all changes that lead from file2 to file3 into file1.
     The result ordinarily goes into file1.
     merge is useful for combining separate changes to an original. 
     Suppose file2 is the original, and both file1 and file3 are modifications of file2. 
     Then merge combines both changes.
   */
  ustring file1 = gw_build_filename(workingdirectory, "file1");
  ustring file2 = gw_build_filename(workingdirectory, "file2");
  ustring file3 = gw_build_filename(workingdirectory, "file3");

  /*
     merge has problems when two consecutive lines are changed, 
     one line in one file and the other line in the other file. 
     Therefore data is going to be cut on the spaces, 
     so that there is one word per line. 
     Each new line is indicated too so as to facilitate joining the loose bits again.
     Another advantage of this is that the merge operation becomes finer grained.
   */

  // Write the data for the common ancestor.
  g_file_set_contents(file2.c_str(), merge_split_data(common_ancestor).c_str(), -1, NULL);

  // Write the data for the main project.
  g_file_set_contents(file1.c_str(), merge_split_data(main_project_data).c_str(), -1, NULL);

  // Write the data for the edited project.
  g_file_set_contents(file3.c_str(), merge_split_data(edited_project_data).c_str(), -1, NULL);

  // Do the three-way merge.
  {
    GwSpawn spawn("merge");
    spawn.workingdirectory(workingdirectory);
    spawn.arg(file1);
    spawn.arg(file2);
    spawn.arg(file3);
    spawn.run();
  }

  // Read the result of the merge.
  ustring merge_result;
  {
    gchar *contents;
    g_file_get_contents(file1.c_str(), &contents, NULL, NULL);
    if (contents) {
      merge_result = contents;
      g_free(contents);
    }
  }

  // Make conflicts human readable.
  merge_result = merge_conflicts_2_human_readable_text(merge_result);

  // Join the bits again.
  merge_result = merge_join_data(merge_result);

  // If there are conflicts, resolve them.
  if (merge_result.find(merge_conflict_markup(1)) != string::npos) {
    MergeDialog dialog(merge_result);
    if (dialog.run() == GTK_RESPONSE_OK) {
      merge_result = dialog.reconciled_text;
    }
  }
  // If there are still conflicts, give a message and bail out.
  if (merge_result.find(merge_conflict_markup(1)) != string::npos) {
    gtkw_dialog_error(NULL, _("The chapters were not merged"));
    return;
  }

  if (approve) {

    // Setup the approval system.
    approval_setup(main_project_data, merge_result);

  } else {

    // Store the merge result in both chapters.  
    ParseLine parseline(merge_result);
    CategorizeChapterVerse ccv(parseline.lines);
    project_store_chapter(current_master_project, book, ccv);
    project_store_chapter(current_edited_project, book, ccv);
    // A normal snapshot may be removed over time, so we need a persistent one to enable future merges.
    snapshots_shoot_chapter (current_master_project, book, chapter, 0, true);
    snapshots_shoot_chapter (current_edited_project, book, chapter, 0, true);

    // Message ok.
    gtkw_dialog_info(NULL, _("The chapters were successfully merged"));

  }
}
void RemoteRepositoryAssistant::on_button_push ()
/*
It copies the existing data, without the .git directory, into the persistent clone,
replaces any data that was there, and then pushes this data to the remote repository.
This makes the remote repository to have an exact copy of our data.
*/
{
  // Progress.
  ProgressWindow progresswindow (_("Pushing your data"), false);
  progresswindow.set_fraction (0.2);
  
  // Copy our data into a temporal location.
  ustring my_data_directory = notes_shared_storage_folder ();
  if (bible_notes_selector_bible ())
    my_data_directory = project_data_directory_project(bible);
  ustring temporal_data_directory = git_testing_directory ("mydata");
  unix_cp_r (my_data_directory, temporal_data_directory);

  // In rare cases a .git directory could have been copied along with our data. Remove that.
  unix_rmdir (gw_build_filename (temporal_data_directory, ".git"));

  // Remove all directories and all files from the persistent clone directory, but leave the .git directory
  {
    ReadDirectories rd (persistent_clone_directory, "", "");
    for (unsigned int i = 0; i < rd.directories.size(); i++) {
      if (rd.directories[i] != ".git") {
        unix_rmdir (gw_build_filename (persistent_clone_directory, rd.directories[i]));
      }
    }
    ReadFiles rf (persistent_clone_directory, "", "");
    for (unsigned int i = 0; i < rf.files.size(); i++) {
      unlink (gw_build_filename (persistent_clone_directory, rf.files[i]).c_str());
    }
  }
  
  // Move our data, from its temporal location, into the persistent clone directory.
  progresswindow.set_fraction (0.4);
  {
    ReadDirectories rd (temporal_data_directory, "", "");
    for (unsigned int i = 0; i < rd.directories.size(); i++) {
      unix_mv (gw_build_filename (temporal_data_directory, rd.directories[i]), persistent_clone_directory);
    }
    ReadFiles rf (temporal_data_directory, "", "");
    for (unsigned int i = 0; i < rf.files.size(); i++) {
      unix_mv (gw_build_filename (temporal_data_directory, rf.files[i]), persistent_clone_directory);
    }
  }

  // Commit the new data in the persistent clone directory.
  progresswindow.set_fraction (0.55);
  {
    GwSpawn spawn ("git");
    spawn.workingdirectory (persistent_clone_directory);
    spawn.arg ("add");
    spawn.arg (".");
    spawn.run ();
  }
  progresswindow.set_fraction (0.65);
  {
    GwSpawn spawn ("git");
    spawn.workingdirectory (persistent_clone_directory);
    spawn.arg ("commit");
    spawn.arg ("-a");
    spawn.arg ("-m");
    spawn.arg ("user data into repo");
    spawn.run ();
  }

  // Push our data to the remote repository.
  progresswindow.set_fraction (0.8);
  GwSpawn spawn("git");
  spawn.workingdirectory(persistent_clone_directory);
  spawn.arg ("push");
  spawn.run();

  // Take action depending on the outcome of pushing to the remote repository.
  if (spawn.exitstatus == 0) {
    // Clone okay.
    gtk_label_set_text (GTK_LABEL (label_push), _("Your data has been pushed to the remote repository"));
  } else {
    // Clone failed.
    gtk_label_set_text (GTK_LABEL (label_push), _("Your data could not be pushed to the remote repository,\nplease restart the assistant"));
    repository_unclone();
  }  
}
bool RemoteRepositoryAssistant::try_git ()
// Tries git and returns true if everything's fine.
{
  // Progress.
  ProgressWindow progresswindow (_("Trying the contents tracker"), false);
  
  // Whether git is okay.
  bool okay = true;
  
  if (okay) {
    progresswindow.set_fraction (0.05);
    gw_message (_("Check git version number"));
    okay = check_git_version ();
  }
  
  // Clean the directory to work in.
  {
    ustring directory = git_testing_directory ("");
    unix_rmdir (directory);
    gw_mkdir_with_parents (directory);
  }
  
  if (okay) {
    progresswindow.set_fraction (0.11);
    gw_message (_("Create first local repository"));
    okay = try_git_create_repository ("local1", false);
  }

  if (okay) {
    progresswindow.set_fraction (0.17);
    gw_message (_("Store data into first local repository"));
    okay = try_git_store_data_in_repository ("local1", "--test--");
  }

  if (okay) {
    progresswindow.set_fraction (0.23);
    gw_message (_("Create remote repository"));
    okay = try_git_create_repository ("remote", true);
  }

  if (okay) {
    progresswindow.set_fraction (0.29);
    gw_message (_("Fetch data from the first local repository into the remote one"));
    okay = try_git_fetch_repository ("remote", "local1");
  }

  if (okay) {
    progresswindow.set_fraction (0.35);
    gw_message (_("Checkout the first local repository"));
    okay = try_git_checkout_repository ("local1", "remote");
  }

  if (okay) {
    progresswindow.set_fraction (0.41);
    gw_message (_("Check data of first local repository"));
    okay = try_git_check_data_in_repository ("local1", "--test--");
  }

  if (okay) {
    progresswindow.set_fraction (0.47);
    gw_message (_("Checkout the second local repository"));
    okay = try_git_checkout_repository ("local2", "remote");
  }

  if (okay) {
    progresswindow.set_fraction (0.52);
    gw_message (_("Check data of second local repository"));
    okay = try_git_check_data_in_repository ("local2", "--test--");
  }

  if (okay) {
    progresswindow.set_fraction (0.58);
    gw_message (_("Store different data into first repository"));
    okay = try_git_store_data_in_repository ("local1", "---test---");
  }

  if (okay) {
    progresswindow.set_fraction (0.64);
    gw_message (_("Push first repository"));
    okay = try_git_push_repository ("local1");
  }

  if (okay) {
    progresswindow.set_fraction (0.70);
    gw_message (_("Pull second repository"));
    okay = try_git_pull_repository ("local2");
  }

  if (okay) {
    progresswindow.set_fraction (0.76);
    gw_message ("Check data in second repository");
    okay = try_git_check_data_in_repository ("local2", "---test---");
  }

  if (okay) {
    progresswindow.set_fraction (0.82);
    gw_message (_("Store different data into second repository"));
    okay = try_git_store_data_in_repository ("local2", "----test----");
  }

  if (okay) {
    progresswindow.set_fraction (0.88);
    gw_message (_("Push second repository"));
    okay = try_git_push_repository ("local2");
  }

  if (okay) {
    progresswindow.set_fraction (0.94);
    gw_message (_("Pull first repository"));
    okay = try_git_pull_repository ("local1");
  }

  if (okay) {
    progresswindow.set_fraction (1);
    gw_message (_("Check data in first repository"));
    okay = try_git_check_data_in_repository ("local1", "----test----");
  }

  // Return whether git is ok.
  return okay;
}