r-lib / gert

Simple git client for R
https://docs.ropensci.org/gert/
Other
146 stars 31 forks source link

FR: provide an interface to git show to retrieve file contents at commit id #219

Open olivroy opened 3 months ago

olivroy commented 3 months ago

I can currently use

old_file_lines <- system(paste("git show HEAD:", uncommited_file), intern = TRUE)

but I think it would error pretty badly if something goes wrong.

I don't see it https://libgit2.org/libgit2/

The idea is to build a widget that wrap gert and diffviewer to see diffs between my currently uncommited file and its stage prior to commit.

This may exist, but I couldn't find it (I am still not 100% familiar with the more advances / less common git jargon)

FWIW, my wrapper looks like this, but I find it a bit clumsy, and hard to follow (+ unsafe).

  git_visual_diff <- function(uncommited_file, repo = ".") {
    rlang::check_installed(c("diffviewer", "gert", "xfun", "fs"))
      uncommited_file
      if (is.null(uncommited_file)) {
        cli::cli_abort("Not for unsaved documents.")
      }
      uncommited_file <- fs::path_rel(
        fs::path_real(uncommited_file),
        fs::path_real(repo)
      )
      if (stringr::str_detect(uncommited_file, "../")) {
        cli::cli_abort("uncommited file can't be outside repo.")
      }
      if (!fs::is_file(uncommited_file)) {
        cli::cli_abort("{.arg uncommited file} must be an existing file.")
      }
      if (is.na(gert::git_stat_files(uncommited_file)$modified)) {
        # Solution
        cli::cli_inform("New (uncommited) file {.run [Click to delete])(fs::file_delete({as.character(uncommited_file)}))}.")
        tmp_dir <- withr::local_tempdir()
        empty_file <- fs::path(tmp_dir, uncommited_file)
        xfun::write_utf8(
          text = "",
          empty_file
        )
        return(diffviewer::visual_diff(
          file_old = empty_file,
          file_new = uncommited_file,
          height = 100
        ))
      }
      if (repo != ".") {
        # change dir temporarily
        withr::with_dir(repo, {
          old_file_lines <- system(paste("git show HEAD:", uncommited_file), intern = TRUE)
        })
      } else {
        old_file_lines <- system(paste0("git show HEAD:", uncommited_file, "\""), intern = TRUE)
      }

      tmp_dir <- withr::local_tempdir()
      file_old <- fs::path(tmp_dir, paste0(fs::path_ext_remove(fs::path_file(uncommited_file)), "-uncommited"), ext = fs::path_ext(uncommited_file))
      xfun::write_utf8(
        con = file_old,
        text = old_file_lines
      )
      diffviewer::visual_diff(
        file_old = file_old,
        file_new = uncommited_file,
        height = 100
      )
    }
  }

There is probably previous art, but I wasn't satisfied with the ones I could find. I would also welcome references if you can think of something too!

gert::git_diff can be useful, for printing diff to the console, but it will show as bland, i.e. no color. Somewhat relates to #152

olivroy commented 3 months ago

Maybe if gert implemented a print method, it would be great?