Извлечь конкретную версию на основе даты фиксации с помощью JGit

Я хочу использовать библиотеку Java JGit для получения ранее зафиксированных версий файла. Файл обновляется ежедневно, и мне нужен доступ к предыдущим версиям из репозитория git.

Я знаю, как проверить конкретную версию, предоставив хэш фиксации нужной мне версии, как описано здесь с помощью этой команды:

git.checkout().setName( "<id-to-commit>" ).call();

В настоящее время я клонирую репозиторий, ищу самую последнюю фиксацию этого файла, а затем проверяю версию. Процесс, который у меня сейчас есть, громоздкий, так как мне нужно будет каждый раз повторно проверять репозиторий. Я бы предпочел просто экспортировать один файл из определенной версии (например, основной ветки).

Журнал изменений

Получить журнал изменений репозитория и получить коммиты интересующего меня файла.

 private static HashMap getRevisionsByLog(Repository repository, String filePath) {


    HashMap commitMap = new HashMap<String, DateTime >();


    Git git = new Git(repository);
    LogCommand logCommand = null;
    try {
        logCommand = git.log()
                .add(git.getRepository().resolve(Constants.HEAD))
                .addPath(filePath);
    } catch (IOException e) {
        e.printStackTrace();
    }

    try {
        for (RevCommit revCommit : logCommand.call()) {
            DateTime commitDate = new DateTime(1000L * revCommit.getCommitTime());
            // Store commit hash and date
            commitMap.put(revCommit.getName(),commitDate);
        }
    } catch (GitAPIException e) {
        e.printStackTrace();
    }

    return commitMap;
}

Самая последняя редакция

Этот код извлекает самую последнюю версию коммитов, предшествующую интересующей меня дате:

 private static String getMostRecentCommit(HashMap<String, DateTime > commitMap, DateTime execDate){
    Iterator it = commitMap.entrySet().iterator();
    Map.Entry<String, DateTime> mostRecentCommit = null;

    while (it.hasNext()) {
        Map.Entry pair = (Map.Entry)it.next();
        DateTime currentCommitDate = (DateTime) pair.getValue();

        if(mostRecentCommit==null && ((DateTime) pair.getValue()).isBefore(execDate)){

           mostRecentCommit = pair;
            }else if (currentCommitDate.isBefore(execDate)){
                System.out.println("Current date is before exec");
                if(currentCommitDate.isAfter(mostRecentCommit.getValue())){
                    System.out.println("Current date is before exec and after the most recent one");
                    mostRecentCommit=pair;
                }
            }

    }

    System.out.println("Current most recent:  " + mostRecentCommit.getKey() + " = " + mostRecentCommit.getValue());
    return mostRecentCommit.getKey();

}

Простое тестовое приложение

В этом приложении я хочу вернуть рабочую копию файла в состояние, которое было до 2015-06-26T14:25:00.

    public static void main(String[] args) {

    DateTime dt = new DateTime("2015-06-26T14:25:00");
    Date execDate = dt.toDate();

    String remotePath = "/media/Data/Gittest/repo/";
    String localPath="/tmp/localgit";

    // Clone repository
    try {
        Git.cloneRepository().setURI(remotePath)
                .setDirectory(new File(localPath)).call();
    } catch (GitAPIException e) {
        e.printStackTrace();
    }


    Git git = null;
    try {
        git = Git.open(new File(localPath));
        Repository repo = git.getRepository();

        HashMap map = getRevisionsByLog(repo, "versuch.txt");

        String commitID = getMostRecentCommit(map,dt);

        System.out.println("Commit hash" +commitID);
        git.checkout().setName(commitID).call();



    } catch (IOException e) {
       ...
    }


}

Вопросы

Это очень наивная реализация, я уверен, что JGit предлагает более изящный способ, но мне не удалось найти никаких подсказок. Я хотел бы программно экспортировать конкретную версию одного файла. Мне нужно сослаться на конкретную дату, я хочу получить версию, которая была действительна в течение этой даты. Сам файл обновляется довольно часто, тем не менее мне нужен механизм для доступа к предыдущим версиям и использования их в качестве входных данных для другого процесса. Каков наилучший способ добиться этого с помощью Java и JGit?

Решение, основанное на ответе Рюдигера Херрманна

Предложенное вами решение работает только с точной датой/временной меткой. Таким образом, как вы сказали в своем ответе, решение работает только в том случае, если в день происходит только одна фиксация. Поскольку это не гарантируется, я использую немного другой подход.

Сначала я извлекаю все коммиты из файла и сортирую их по дате в Treemap. Коммиты сортируются в порядке убывания, поэтому самый последний — последний.

    /*
    * Get all commits of a file before a given date
    * */
    private TreeMap<DateTime,RevCommit> getAllCommitsBefore(Date execDate, String path){
    RevWalk walk = new RevWalk( this.repo );
    TreeMap<DateTime, RevCommit> commitsByDate = new TreeMap<DateTime, RevCommit>();
    try {
        walk.markStart( walk.parseCommit( this.repo.resolve( Constants.HEAD ) ) );

        walk.sort( RevSort.COMMIT_TIME_DESC);
        walk.setTreeFilter(PathFilter.create(path));

        for( RevCommit commit : walk ) {
            if ( commit.getCommitterIdent().getWhen().before(execDate) ) {
                DateTime commitTime = new DateTime(commit.getCommitterIdent().getWhen());
                commitsByDate.put(commitTime, commit);

            }
        }
        walk.close();
        System.out.println("Number of valid commits: " + commitsByDate.size());
    } catch (IOException e) {
        e.printStackTrace();
    }
    return commitsByDate;
    }

Затем я могу получить самую последнюю фиксацию, просто получив последнюю фиксацию в TreeMap.

    public RevCommit getMostRecentCommit(Date execDate, String path){
    TreeMap<DateTime,RevCommit> allCommits = this.getAllCommitsBefore(execDate,path);
    RevCommit lastCommit = allCommits.lastEntry().getValue();
    System.out.println("Last entry: " + lastCommit.getName());
    return lastCommit;

    }

Затем я могу получить файл этой версии и экспортировать его.

    public void retrieveFileFromCommit(String path, RevCommit commit, String outputPath){
    TreeWalk treeWalk  = null;
    try {
        treeWalk = TreeWalk.forPath(this.repo, path, commit.getTree());
        InputStream inputStream = this.repo.open( treeWalk.getObjectId( 0 ), Constants.OBJ_BLOB ).openStream();

        this.writeFile(inputStream,outputPath);
        treeWalk.close(); // use release() in JGit < 4.0

    } catch (IOException e) {
        e.printStackTrace();
    }

    }

    /*
    * Write the stream to the disk
    * */
    private void writeFile(InputStream inputStream, String outputPath){
    OutputStream outputStream =
            null;
    try {
        outputStream = new FileOutputStream(new File(outputPath));
        int read = 0;
        byte[] bytes = new byte[1024];

        while ((read = inputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0, read);
        }

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }


    System.out.println("Done!");

    }

person Stefan    schedule 26.06.2015    source источник


Ответы (1)


Если я правильно понимаю ваш вопрос, вы хотите найти фиксацию в заданном диапазоне дат, а затем прочитать содержимое определенной формы файла, которая фиксирует.

Предполагая, что на дату существует только одна фиксация, вы можете использовать RevWalk, чтобы получить желаемую фиксацию:

try (RevWalk walk = new RevWalk(repo)) {
  walk.markStart(walk.parseCommit(repo.resolve(Constants.HEAD)));
  walk.sort(RevSort.COMMIT_TIME_DESC);
  walk.setRevFilter(myFilter);
  for (RevCommit commit : walk) {
    if (commit.getCommitter().getWhen().equals(date)) {
      // this is the commit you are looking for
      revWalk.parseCommit(commit);
      break;
    }
  }
}

Я не совсем уверен, нужен ли revWalk.parseCommit(commit); - это зависит от того, как настроен RevWalk. Попробуйте запустить код без разбора коммита, и если файл найден, оставьте все как есть.

Теперь, когда у вас есть нужный коммит, используйте TreeWalk для получения содержимого файла:

try (TreeWalk treeWalk = TreeWalk.forPath(repository, fileName, commit.getTree())) {
  InputStream inputStream = repository.open(treeWalk.getObjectId(0), Constants.OBJ_BLOB).openStream();
  // use the inputStream
}

fileName содержит путь относительно репозитория к рассматриваемому файлу.

person Rüdiger Herrmann    schedule 26.06.2015