jgit repository browser - java

I would like to create a git repository browser with jgit. But i don't know how to get the last modified date and the last commit message for a file. Here is my current code for the browser:
File directory = new File("/Users/sdorra/.scm/repositories/git/scm-git");
Repository repository =
RepositoryCache.open(RepositoryCache.FileKey.lenient(directory,
FS.DETECTED), true);
try
{
ObjectId revId = repository.resolve(Constants.HEAD);
DirCache cache = new DirCache(directory, FS.DETECTED);
TreeWalk treeWalk = new TreeWalk(repository);
treeWalk.addTree(new RevWalk(repository).parseTree(revId));
treeWalk.addTree(new DirCacheIterator(cache));
while (treeWalk.next())
{
System.out.println("---------------------------");
System.out.append("name: ").println(treeWalk.getNameString());
System.out.append("path: ").println(treeWalk.getPathString());
ObjectLoader loader = repository.open(treeWalk.getObjectId(0));
System.out.append("directory: ").println(loader.getType()
== Constants.OBJ_TREE);
System.out.append("size: ").println(loader.getSize());
// ???
System.out.append("last modified: ").println("???");
System.out.append("message: ").println("???");
}
}
finally
{
if (repository != null)
{
repository.close();
}
}
It is possible to get the last commit of a file?
Note: My git repository is a bare repository without working copy.

You're using lower level JGit API, why don't you use LogCommand via the org.eclipse.jgit.api package? Then use addPath(...), call()...
After that, you should get a list of RevCommit's for the specified path.

Related

How to create a branch and raise a Pull Request on github using org.eclipse.egit.github.core library?

I am trying to write a java code , where I can update a file , and create a branch and then a PR out of this Branch in github. I am using this below library.
<!-- https://mvnrepository.com/artifact/org.eclipse.mylyn.github/org.eclipse.egit.github.core -->
<dependency>
<groupId>org.eclipse.mylyn.github</groupId>
<artifactId>org.eclipse.egit.github.core</artifactId>
<version>2.1.5</version>
</dependency>
I am able to connect to github using GitHubClient.java , and fetched the repository. But not able to create a branch and raise a PR through this library, I am not sure which class/method to use for creating branch and raise the PR
private String loadRepository(GitHubClient client, Repository repo) throws IOException {
System.out.println("\n\nScanning repository " + repo.getName());
ContentsService contentService = new ContentsService(client);
List<RepositoryContents> test = contentService.getContents(repo, "/pom.xml");
for (RepositoryContents content : test) {
String fileConent = content.getContent();
String pomXml = new String(Base64.decodeBase64(fileConent.getBytes()));
String s = t1(loadXMLFromString(pomXml)); // Updated the pom file, stroed in var s
// TODO , create a branch with this updated pom and raise PR
return s;
}
return null;
}

How to programmatically create a GitHub repository?

I have to use Java to programmatically create a GitHub repository and push code to it. Please advise on the best method to use. Also please share any code snippet or related links for the utility.
I referred jgit library, has anyone used it? I also referred hub, gh and command line utility.
You can use the GitHub Rest API.
Generate a Personal Access Token from Settings > Developers Settings > Personal Access Tokens
Once generated use that to call the endpoint -
https://api.github.com/user/repo
with body
{"name": "REPO_NAME"}
and Header
Authorization: token PERSONAL_ACCESS_TOKEN
Example Curl:
curl -H "Authorization: token PERSONAL_ACCESS TOKEN" https://api.github.com/user/repos -d '{"name": "REPO_NAME"}'
Reference Doc: https://docs.github.com/en/rest/repos/repos?apiVersion=2022-11-28#create-a-repository-for-the-authenticated-user
The gitlab4j api is a great library to do so : https://github.com/gitlab4j/gitlab4j-api
<!-- https://mvnrepository.com/artifact/org.gitlab4j/gitlab4j-api -->
<dependency>
<groupId>org.gitlab4j</groupId>
<artifactId>gitlab4j-api</artifactId>
<version>5.0.1</version>
</dependency>
I'm using to read files from repo, commit and create merge requests :
try {
var branchName = "BRANCH-" + random.nextLong();
var action = new CommitAction();
action.withContent(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(data))
.withFilePath(JSON_FILE_PATH).setAction(Action.UPDATE);
try (var glapi = new GitLabApi(GIT_HOSTNAME, token)) {
glapi.enableRequestResponseLogging(java.util.logging.Level.INFO);
glapi.getCommitsApi().createCommit(REPO_NAME, branchName, message, VERSION, null, null, action);
return glapi.getMergeRequestApi().createMergeRequest(DB_REPO_NAME, branchName, VERSION, message,
message, ADMIN_USER_ID);
}
} catch (Exception ex) {
throw new DataException(ex);
}
how to create a new repo, a new master branch and your first commit :
try (var glapi = new GitLabApi("https://gitlab.com/", token)) {
var projectApi = glapi.getProjectApi();
var project = projectApi.createProject("my-repo");
// my-repo created
var action = new CommitAction();
action.withContent("### ignore some files ###").withFilePath(".gitignore").setAction(Action.CREATE);
glapi.getCommitsApi().createCommit("my-repo", "master", "my first commit", "master", "author#mail.com",
"author", action);
// yoru first commit
}
for github you can use https://github.com/hub4j/github-api
#Test
public void testCreateRepoPublic() throws Exception {
initGithubInstance();
GHUser myself = gitHub.getMyself();
String repoName = "test-repo-public";
GHRepository repo = gitHub.createRepository(repoName).private_(false).create();
try {
assertThat(repo.isPrivate(), is(false));
repo.setPrivate(true);
assertThat(myself.getRepository(repoName).isPrivate(), is(true));
repo.setPrivate(false);
assertThat(myself.getRepository(repoName).isPrivate(), is(false));
} finally {
repo.delete();
}
}
commiting multiple files :
var repo = github.getRepository("my-repo");
GHRef mainRef = repo.getRef("heads/master");
String mainTreeSha = repo.getTreeRecursive("master", 1).getSha();
GHTreeBuilder treeBuilder = repo.createTree().baseTree(mainTreeSha);
treeBuilder.add("file1.txt", Files.readAllBytes(Path.of("file1.txt"), false);
treeBuilder.add("file2.json", Files.readAllBytes(Path.of("file2.json"), false);
treeBuilder.add("dir1/dir2/file3.xml", Files.readAllBytes(Path.of("dir1/dir2/file3.xml"), false);
String treeSha = treeBuilder.create().getSha();
GHCommit commit = repo.createCommit()
.tree(treeSha)
.message("adding multiple files example")
.author("author", "author#mail.com", new Date())
.committer("committer", "committer#mail.com", new Date())
.parent(mainRef.getObject().getSha())
.create();
String commitSha = commit.getSHA1();
mainRef.updateTo(commitSha);
Using eclipse jgit
<!-- https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit -->
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>6.2.0.202206071550-r</version>
</dependency>
Examples :
// to use basic auth
var credentials = new UsernamePasswordCredentialsProvider("user", "password");
// to clone a repository
Git.cloneRepository().setURI("https://github.com/my-repo.git").setCredentialsProvider(credentials).call();
// to init a new repository
Git.init().setDirectory(new File("/opt/my-repo")).setInitialBranch("master").call();
// to use a cloned repo
var git = Git.open(new File("/opt/my-repo"));
// to pull changes from remote
git.pull().setCredentialsProvider(credentials).call();
// to stage one file
git.add().setUpdate(true).addFilepattern("dir/file.txt").call();
// to stage all files in a directory
git.add().setUpdate(true).addFilepattern("dir").call();
// to create a commit with the staged files
git.commit().setMessage("just adding some files.").call();
// to push changes to remote
git.push().call();
I tried the jgit to add files to the GitHub repo. Commit is getting created, but the files are not added, In addfilepattern - I tried "." as well. Below is my code snippet:
Git.cloneRepository().setURI("https://github.com/"+org+"/"+repo+".git").setCredentialsProvider(credentialsProvider).setDirectory(new File(Gitpath)).setCloneAllBranches(true).call();
Git.init().setDirectory(new File(Gitpath)).setInitialBranch("main").call();
var git = Git.open(new File(Gitpath));
git.add().setUpdate(true).addFilepattern(directoryName+fileName).call();
git.commit().setMessage("Adding files.").call();
git.push().setCredentialsProvider(credentialsProvider).call();

fetching files from SFTP

I want to fetch files from SFTP which are created after a given timestamp(time of last pull) in java. I am using j2ssh as of now. Please let me know if some other API supports such a feature.
Jsch supports the ls command which will bring you back all the attributes of the remote file. You can write a little code to eliminate the files you want to retrieve from there.
Java Doc: http://epaul.github.io/jsch-documentation/javadoc/
This example compares the remote file timestamps to find the oldest file, it wouldn't be much of a stretch to modify it to compare your last run date against the remote file date, then do the download as part of the loop.
Code from Finding file size and last modified of SFTP file using Java
try {
list = Main.chanSftp.ls("*.xml");
if (list.isEmpty()) {
fileFound = false;
}
else {
lsEntry = (ChannelSftp.LsEntry) list.firstElement();
oldestFile = lsEntry.getFilename();
attrs = lsEntry.getAttrs();
currentOldestTime = attrs.getMTime();
for (Object sftpFile : list) {
lsEntry = (ChannelSftp.LsEntry) sftpFile;
nextName = lsEntry.getFilename();
attrs = lsEntry.getAttrs();
nextTime = attrs.getMTime();
if (nextTime < currentOldestTime) {
oldestFile = nextName;
currentOldestTime = nextTime;
}
}

How do I do “git show sha1” using JGit

Basically I would like to read the contents of all files in a commit based on the commit hash.
I've tried the following:
try(RevWalk revWalk = new RevWalk(gitRepository))
{
RevCommit commit = revWalk.parseCommit(ObjectId.fromString(commitSha));
RevTree tree = commit.getTree();
try(TreeWalk treeWalk = new TreeWalk(gitRepository))
{
treeWalk.addTree(tree);
treeWalk.setRecursive(true);
ObjectId entryId = null;
while (treeWalk.next())
{
entryId = treeWalk.getObjectId(0);
}
ObjectLoader loader = gitRepository.open(entryId);
}
revWalk.dispose();
}
but it seems to be picking up files from previous commits as well.
EDIT: I realize that I wasn't very specific in my original post.
Let's say I make a commit (Commit1) where I add a file (File1). Then I make a commit (Commit2) where I add a different file (File2). Then I make another commit (Commit3) where I modified File2. I would now like to get the contents of File2 from Commit2 for whatever reason. Using the above, the treewalk will retrieve the contents of Commit2 AND Commit1 which is not what I want.
As you've noticed, Git does not store a commit as a diff to the prior commit, it stores a commit as a snapshot of the entire repository at that point in time.
This is not terribly obvious, because even git show <commitid> will provide you with a diff between a commit and its parent. But it becomes clear when you iterate over the contents of a commit like you've done.
If you want to emulate git show <commitid> and look at what changes were introduced by a commit, you'll need to compare it to its parent.
Git git = new Git(gitRepository);
ObjectId newTreeId = ObjectId.fromString(commitSha + "^{tree}");
ObjectId oldTreeId = gitRepository.resolve(commitSha + "^^{tree}");
CanonicalTreeParser newTree = new CanonicalTreeParser();
newTree.reset(reader, newTreeId);
CanonicalTreeParser oldTree = new CanonicalTreeParser();
oldTree.reset(reader, oldTreeId);
for (DiffEntry de : git.diff().setNewTree(newTree).setOldTree(oldTree).call())
{
/* Print the file diff */
DiffFormatter formatter = new DiffFormatter(System.out);
formatter.setRepository(gitRepository);
formatter.format(de);
}

JGit detect rename in working copy

Contex
I'm trying to detect possible file rename that occurred after last commit, in a working copy.
On my example, I have a clean working copy and I do that:
git mv old.txt new.txt
Running $ git status shows the expected result:
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# renamed: old.txt -> new.txt
I tried
Using a StatusCommand, I can see old.txt in the removed list, and new.txt in the added list.
But I can't find a way to link them together.
I'm aware of the existence of RenameDetector, but it works using DiffEntry, and I don't know how to get DiffEntries between HEAD and the Working Copy.
Never mind, found the answer.
JGit's API is very complicated..
TreeWalk tw = new TreeWalk(repository);
tw.setRecursive(true);
tw.addTree(CommitUtils.getHead(repository).getTree());
tw.addTree(new FileTreeIterator(repository));
RenameDetector rd = new RenameDetector(repository);
rd.addAll(DiffEntry.scan(tw));
List<DiffEntry> lde = rd.compute(tw.getObjectReader(), null);
for (DiffEntry de : lde) {
if (de.getScore() >= rd.getRenameScore()) {
System.out.println("file: " + de.getOldPath() + " copied/moved to: " + de.getNewPath());
}
}
(This snippet also use Gitective library)
In a case that someone wants to use path filter when getting DiffEntry, new and old path should be provided.
List<DiffEntry> diffs = git.diff()
.setOldTree(prepareTreeParser(repository, oldCommit))
.setNewTree(prepareTreeParser(repository, newCommit))
.setPathFilter(PathFilterGroup.createFromStrings(new String[]{"new/b.txt","b.txt"}))
.call();
RenameDetector rd = new RenameDetector(repository);
rd.addAll(diffs);
diffs = rd.compute();
If you want code of tree parser method:
private static AbstractTreeIterator prepareTreeParser(Repository repository, String objectId) throws IOException {
try (RevWalk walk = new RevWalk(repository)) {
RevCommit commit = walk.parseCommit(repository.resolve(objectId));
RevTree tree = walk.parseTree(commit.getTree().getId());
CanonicalTreeParser treeParser = new CanonicalTreeParser();
try (ObjectReader reader = repository.newObjectReader()) {
treeParser.reset(reader, tree.getId());
}
walk.dispose();
return treeParser;
}
}

Categories

Resources