2011-01-24 13 views
8

He conseguido llegar a enfrentarse con los conceptos básicos de archivo jGit en términos de conexión a un repos y añadiendo, cometer, e incluso bucle de los mensajes de confirmación para los archivos.bucle sobre comete un archivo con jGit

File gitDir = new File("/Users/myname/Sites/helloworld/.git"); 

RepositoryBuilder builder = new RepositoryBuilder(); 
Repository repository; 
repository = builder.setGitDir(gitDir).readEnvironment() 
     .findGitDir().build(); 

Git git = new Git(repository); 
RevWalk walk = new RevWalk(repository); 
RevCommit commit = null; 

// Add all files 
// AddCommand add = git.add(); 
// add.addFilepattern(".").call(); 

// Commit them 
// CommitCommand commit = git.commit(); 
// commit.setMessage("Commiting from java").call(); 

Iterable<RevCommit> logs = git.log().call(); 
Iterator<RevCommit> i = logs.iterator(); 

while (i.hasNext()) { 
    commit = walk.parseCommit(i.next()); 
    System.out.println(commit.getFullMessage()); 

} 

Lo que quiero hacer ahora es ser capaz de obtener todo el mensaje de confirmación para un solo archivo y luego ser capaz de revertir la fila india de nuevo a una referencia/punto específico en el tiempo.

Respuesta

9

Aquí es cómo encontrar los cambios de una confirmación sobre la base de todos los padres comete

 var tree = new TreeWalk(repository) 
     tree.addTree(commit.getTree) 
     commit.getParents foreach { 
      parent => tree.addTree(parent.getTree) 
     } 
     tree.setFilter(TreeFilter.ANY_DIFF) 

(código Scala)

Tenga en cuenta que TreeFilter.ANY_DIFF funciona para un solo árbol andador y se devuelve todos los elementos disponibles en una confirmación raíz.

A continuación, tendría para iterar sobre el árbol para ver si el archivo se encuentra en el delta dado (esto es bastante fácil).

while (tree.next) 
      if (tree.getDepth == cleanPath.size) { 
       // we are at the right level, do what you want 
      } else { 
       if (tree.isSubtree && 
        name == cleanPath(tree.getDepth)) { 
        tree.enterSubtree 
       } 
      } 
    } 

(CleanPath es la pura en la trayectoria de recompra, dividido por '/')

Ahora envuelva a ese código en un bucle RevWalk.next y obtendrá las confirmaciones y los archivos alterados por la confirmación.

Es posible que desee utilizar un filtro diferente que ANY_DIFF, porque ANY_DIFF es cierto si difiere uno de los árboles. Esto es un poco contra-intuitivo en el caso de una fusión donde el blob no cambió en comparación con todos los árboles padre. Así que aquí es el núcleo de un ALL_DIFF, que sólo mostrará los elementos que difieren de todos los árboles del padre:

override def include(walker: TreeWalk): Boolean = { 
    val n = walker.getTreeCount(); 
    if (n == 1) { 
     return true; 
    } 
    val m = walker.getRawMode(0) 
    var i = 1 
    while (i < n) { 
     if (walker.getRawMode(i) == m && walker.idEqual(i, 0)) { 
      return false 
     } 
     i += 1 
    } 
    true 
} 

(código Scala, derivado de AnyDiffFilter)

+0

Gracias, esto realmente me ayudó. +1 – Dylan

2

El enfoque general con git para encontrar el historial de un archivo específico es recorrer el gráfico de revisión (que está haciendo) y para cada uno, probar el objeto al que hace referencia la ruta en cuestión (puede ser un blob o un árbol para encontrar el historial de un subárbol completo). Así que, en esencia, actúa como un filtro en la salida del conjunto de revisión por el caminante de revisión.

La documentación jgit parece ... escasa. Pero deberías poder obtener un RevTree correspondiente a cada RevCommit, y si necesariamente pasas por eso con cada segmento de camino, a su vez a un ID de objeto terminal.

1

araqnid es correcto, esto es cómo llegué a una lista de fechas, cada fecha relativa a comprometerse que incluía el archivo en cuestión ...

A continuación, puede recuperar el archivo desde una específica comprometerse y cuando tenga la nombre del archivo y la fecha de la confirmación, vea los dos métodos a continuación ....

nota: este código está en una clase .groovy, por lo que sin duda tendrá que modificar un poco para Java.

byte[] getAnyPreviousVersionFileBytes(String relativeFilePath, Date date) { 

    byte[] bytes = null 
    try { 

     RevWalk revWalk = new RevWalk(repository) 
     ObjectId headId = repository.resolve(Constants.HEAD); 
     RevCommit root = revWalk.parseCommit(headId); 

     revWalk.sort(RevSort.COMMIT_TIME_DESC); 
     revWalk.markStart(root); 

     for (RevCommit revCommit: revWalk) { 

      // if date matches then walk the tree in this commit 
      if (new Date(revCommit.commitTime * 1000L) == date) { 

       TreeWalk treeWalk = TreeWalk.forPath(repository, relativeFilePath, revCommit.getTree()) 

       if (treeWalk != null) { 
        treeWalk.setRecursive(true) 
        CanonicalTreeParser canonicalTreeParser = treeWalk.getTree(0, CanonicalTreeParser) 

        while (!canonicalTreeParser.eof()) { 

         // if the filename matches, we have a match, so set teh byte array to return 
         if (canonicalTreeParser.getEntryPathString() == relativeFilePath) { 
          ObjectLoader objectLoader = repository.open(canonicalTreeParser.getEntryObjectId()) 
          bytes = objectLoader.bytes 
         } 
         canonicalTreeParser.next(1) 
        } 
       } 
      } 

     } 

    } 
    catch (Exception e) { 
     throw new JgitException(e) 
    } 
    return bytes 
} 

List<Date> getFileVersionDateList(String relativeFilePath) { 

    List<Date> versions = new LinkedList<Date>() 
    try { 

     RevWalk revWalk = new RevWalk(repository) 
     ObjectId headId = repository.resolve(Constants.HEAD); 
     RevCommit root = revWalk.parseCommit(headId); 

     revWalk.sort(RevSort.COMMIT_TIME_DESC); 
     revWalk.markStart(root); 

     for (RevCommit revCommit: revWalk) { 

      TreeWalk treeWalk = TreeWalk.forPath(repository, relativeFilePath, revCommit.getTree()) 

      if (treeWalk != null) { 
       treeWalk.setRecursive(true) 
       CanonicalTreeParser canonicalTreeParser = treeWalk.getTree(0, CanonicalTreeParser) 

       while (!canonicalTreeParser.eof()) { 
        // if the filename matches, we have a match, so add the date of this commit to the list 
        if (canonicalTreeParser.getEntryPathString() == relativeFilePath) { 
         versions.add(new Date(revCommit.commitTime * 1000L)) 
        } 
        canonicalTreeParser.next(1) 
       } 
      } 
     } 
    } 
    catch (Exception e) { 
     throw new JgitException(e) 
    } 

    return versions 
} 
6

Así que trataron de obtener una solución de charlieboy funcione, y lo hizo principalmente, pero falló en el siguiente caso (¿quizás algo cambió en jgit desde esa publicación?)

complemento archivoA, comprometerse como "cometer 1" añadir FILEB, cometió como "comprometerse 2"

getFileVersionDateList("fileA") 

Tanto commit 1 y commit 2 se encontraron, donde esperaba solamente commit 1.

Mi solución fue la siguiente:

List<Commit> commits = new ArrayList<Commit>(); 

RevWalk revWalk = new RevWalk(repository); 
revWalk.setTreeFilter(
     AndTreeFilter.create(
       PathFilterGroup.createFromStrings(<relative path in question>), 
       TreeFilter.ANY_DIFF) 
); 

RevCommit rootCommit = revWalk.parseCommit(repository.resolve(Constants.HEAD)); 
revWalk.sort(RevSort.COMMIT_TIME_DESC); 
revWalk.markStart(rootCommit); 

for (RevCommit revCommit : revWalk) { 
    commits.add(new GitCommit(getRepository(), revCommit)); 
} 

Utilizando el LogCommand es aún más simple, y se parece a esto:

List<Commit> commitsList = new ArrayList<Commit>(); 

Git git = new Git(repository); 
LogCommand logCommand = git.log() 
     .add(git.getRepository().resolve(Constants.HEAD)) 
     .addPath(<relative path in question>); 

for (RevCommit revCommit : logCommand.call()) { 
    commitsList.add(new GitCommit(this, revCommit)); 
} 

Obviamente también se comprobaría las fechas cometer, etc., según sea necesario .

+0

Esta solución es mejor que la respuesta real, para mí. – webmaster

+0

código realmente genial y limpio, ayudó mucho, felicitaciones – Olix

Cuestiones relacionadas