Verifique una revisión específica basada en la date de compromiso con JGit

Quiero utilizar la biblioteca Java JGit para recuperar revisiones previamente comprometidas de un file. El file se actualiza diariamente y necesito tener acceso a las versiones anteriores de un repository git.

Sé cómo verificar una revisión específica al proporcionar el hash de confirmación de la revisión que necesito, como se describe aquí con este command:

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

Actualmente, clono el repository, busco la confirmación más reciente de ese file y luego reviso la revisión. El process que tengo ahora es engorroso ya que necesitaría volver a verificar el repository cada vez. Prefiero simplemente exportar un solo file de una revisión específica (de la twig principal, por ejemplo).

Registro de revisiones

Recupere el logging de revisión del repository y obtenga las confirmaciones del file que me interesa.

  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; } 

Revisión más reciente

Este código recupera la revisión más reciente de las confirmaciones, que es anterior a la date en la que estoy interesado:

  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(); } 

Una aplicación de testing simple

En esta aplicación, deseo revertir la copy de trabajo del file al estado que tenía antes del 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) { ... } } 

Preguntas

Esta es una implementación muy ingenua, estoy seguro de que JGit ofrece una forma más elegante, pero no pude encontrar ninguna pista. Me gustaría exportar una revisión específica de un solo file programáticamente. Necesito referirme a una date específica, quiero search la revisión que fue válida durante esa date. El file en sí mismo se actualiza con bastante frecuencia, sin embargo, necesito un mecanismo para acceder a las versiones anteriores y usarlas como input para otro process. ¿Cuál sería la mejor manera de lograr esto con Java y JGit?

Solución basada en la respuesta de Rüdiger Herrmann

La solución que propuso solo funciona con la date / hora exacta. Por lo tanto, como dijiste en tu respuesta, la solución solo funciona si solo hay una confirmación por día. Como esto no está garantizado, utilizo un enfoque ligeramente diferente.

Primero, recupero todas las confirmaciones del file y las clasifico por date en un Treemap. Los commits se orderan en order descendente, el más reciente es por lo tanto último.

  /* * 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; } 

Luego, puedo recuperar la confirmación más reciente simplemente recuperando la última confirmación en 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; } 

Luego, puedo recuperar el file de esa revisión y exportarlo.

  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!"); } 

Si entiendo su pregunta correctamente, quiere encontrar una confirmación dentro de un range de dates determinado y luego leer el contenido de un file específico que se compromete.

Suponiendo que solo hay una confirmación por date, puede usar RevWalk para get la confirmación deseada:

 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; } } walk.close(); 

No estoy del todo seguro si el revWalk.parseCommit( commit ); es necesario, depende de cómo esté configurado RevWalk . Intenta ejecutar el código sin analizar la confirmación y, si se encuentra el file, déjalo así.

Ahora que tiene la confirmación deseada, use TreeWalk para get el contenido del file:

 TreeWalk treeWalk = TreeWalk.forPath( repository, fileName, commit.getTree() ); InputStream inputStream = repository.open( treeWalk.getObjectId( 0 ), Constants.OBJ_BLOB ).openStream(); // use the inputStream treeWalk.close(); // use release() in JGit < 4.0 

fileName contiene la ruta relativa al repository para el file en cuestión.