Looping sopra si impegna per un file con jGit
Domanda
sono riuscito a fare i conti con le basi del file di jGit in termini di connessione a un pronti contro termine e l'aggiunta, commettere, e anche looping del commit messaggi per i file.
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() );
}
Quello che voglio fare dopo è essere in grado di ottenere tutto il messaggio di commit per un singolo file e quindi essere in grado Revert retro singolo file a un riferimento specifico / punto nel tempo.
Soluzione
Ecco come trovare i cambiamenti di una commit sulla base di tutti i commit genitore
var tree = new TreeWalk(repository)
tree.addTree(commit.getTree)
commit.getParents foreach {
parent => tree.addTree(parent.getTree)
}
tree.setFilter(TreeFilter.ANY_DIFF)
(codice Scala)
Si noti che TreeFilter.ANY_DIFF lavora per un unico albero camminatore e tornerà tutti gli elementi disponibili in una radice commit.
Si dovrebbe quindi per scorrere l'albero per vedere se il file è nella data delta (questo è abbastanza facile).
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 è il puro percorso di pronti contro termine, diviso da '/')
Ora avvolgere quel codice in un ciclo RevWalk.next e otterrete i commit e file alterati dal commit.
Si consiglia di utilizzare un filtro diverso rispetto ANY_DIFF, perché ANY_DIFF è vero se uno è diverso albero. Questo è un po 'contro-intuitivo, in caso di una fusione in cui il blob non sono variate rispetto a tutti gli alberi genitore. Così qui è il cuore di un ALL_DIFF, che mostrerà solo gli elementi che si differenziano da tutti gli alberi genitore:
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
}
(codice Scala, derivato da AnyDiffFilter)
Altri suggerimenti
Così ho cercato di ottenere la soluzione di charlieboy al lavoro, ed è in gran parte fatto, ma è venuto a mancare per me nel caso seguente (forse qualcosa è cambiato in jgit da quel post?)
aggiungere fileA, commettere come "commit 1" aggiungere fileB, impegnarsi come "commettere 2"
getFileVersionDateList("fileA")
sono stati trovati Sia commit 1
e commit 2
, dove mi aspettavo solo commit 1
.
La mia soluzione è stata la seguente:
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));
}
Uso della LogCommand è ancora più semplice, e si presenta come questo:
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));
}
Ovviamente si sarebbe anche controllare il commit date, ecc, a seconda delle necessità.
L'approccio generale con git per trovare la storia di un file specifico è quello di camminare attraverso il grafico di revisione (che si sta facendo) e per ognuno, testare l'oggetto a cui si riferisce il percorso in questione (può essere sia un blob o un albero per trovare la storia di un intero sotto-albero). Quindi, in sostanza, fungere da filtro sull'uscita set di revisione della revisione walker.
La documentazione jgit sembra ... sparse. Ma si dovrebbe essere in grado di ottenere un RevTree corrispondente ad ogni RevCommit, e se necessariamente attraversare che con ogni segmento del percorso a sua volta fino a un ID oggetto terminale.
araqnid è giusto, questo è come ho ottenuto un elenco di date, ogni data relativa a commettere che ha incluso il file in questione ...
Quindi è possibile recuperare il file da una specifica compiere come avete il nome del file e la data del commit, vedere i due metodi indicati ....
Nota: Questo codice è in una classe .groovy, quindi è senza dubbio dovuto modificare un po 'per 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
}