Skip commit if no files are changed in git2/libgit2 - rust

I'm using the git2 crate which wraps libgit2.
I'm adding files to the index whether or not they are changed to stage them for committing.
If there are no files changed in the repo, when I commit, I get an empty commit, that I'd like to avoid. I thought calling index.is_empty() would indicate no changes, but I have a count returned from index.len().
I'd be fine resetting the index or avoid adding files to the index if they are unchanged.
How can I avoid inserting an empty commit? I know the git CLI has support for this, and can disable it with --allow-empty.

I can iterate through the index entries and look for ones that have not changed by calling status_file against the Repository
let repo: Repository = ...;
let index: Index = ...;
let changed_files = index
.iter()
.flat_map(|e| String::from_utf8(e.path))
.map(|e| PathBuf::from(OsString::from(e)))
.flat_map(|p| repo.status_file(&p))
.any(|s| s != Status::CURRENT);

Related

"index not backed by a repository" error from libgit2 when writing tree from index for 'pull' commit

When I do index.write_tree()) (index is an Index resulting from a merge using merge_commits), I get an error "Failed to write tree. the index file is not backed up by an existing repository". I have an existing repository. [It was bare in the original version of this post, but I have changed it to non-bare, and it is still not working.] What am I doing wrong?
More generally, I am trying to implement git pull (a fetch then a merge). In the catch-up case, I understand that after the fetch and the merge, I need to write out a commit. That is what I am trying to do. How do I do that?
Basically, my code is
let index = repo.merge_commits(&our_commit, &their_commit, Some(&MergeOptions::new()))?;
if !index.has_conflicts() {
let new_tree_oid = index.write_tree()?; // error occurs here
let new_tree = repo.find_tree(new_tree_oid)?;
//...
}
This is using the rust git2 crate, which wraps the libgit2 library.
You have an in-memory index, you don't have the repository's index. This is the distinction that is preventing you from writing it.
You have a few options for dealing with an in-memory index:
You can convert it to a tree (and then you could create a commit that uses that tree), using git_index_write_tree_to. The write_tree_to function will let you specify the repository that you want to write to.
You can make it the repository's index, using git_repository_set_index.
Though I would question why you're using git_merge_commits instead of just git_merge, which should take care of all of this for you, including dealing with conflicts. If you're truly doing a git pull emulation, then you'll need to cope with that.

Microsoft.TeamFoundation.Build.WebApi Get Build Status Launched by PR policy

In our pipeline we programmatically create a pull request (PR). The branch being merged into has a policy on it that launches a build. This build takes a variable amount of time. I need to query the build status until it is complete (or long timeout) so that I can complete the PR, and clean up the temp branch.
I am trying to figure out how to get the build that was kicked off by the PR so that I can inspect the status by using Microsoft.TeamFoundation.Build.WebApi, but all overloads of BuildHttpClientBase.GetBuildAsync require a build Id which I don't have. I would like to avoid using the Azure Build REST API. Does anyone know how I might get the Build kicked off by the PR without the build ID using BuildHttpClientBase?
Unfortunately the documentation doesn't offer a lot of detail about functionality.
Answering the question you asked:
Finding a call that provides the single deterministic build id for a pull request doesn't seem to be very readily available.
As mentioned, you can use BuldHttpClient.GetBuildsAsync() to filter builds based on branch, repository, requesting user and reason.
Adding the BuildReason.PullRequest value in the request is probably redundant according to the branch you will need to pass.
var pr = new GitPullRequest(); // the PR you've received after creation
var requestedFor = pr.CreatedBy.DisplayName;
var repo = pr.Repository.Id.ToString();
var branch = $"refs/pull/{pr.PullRequestId}/merge";
var reason = BuildReason.PullRequest;
var buildClient = c.GetClient<BuildHttpClient>();
var blds = await buildClient.GetBuildsAsync("myProject",
branchName: branch,
repositoryId: repo,
requestedFor: requestedFor,
reasonFilter: reason,
repositoryType: "TfsGit");
In your question you mentioned wanting the build (singular) for the pull request, which implies that you only have one build definition acting as the policy gate. This method can return multiple Builds based on the policy configurations on your target branch. However, if that were your setup, it would seem logical that your question would then be asking for all those related builds for which you would wait to complete the PR.
I was looking into Policy Evaluations to see if there was a more straight forward way to get the id of the build being run via policy, but I haven't been able to format the request properly as per:
Evaluations are retrieved using an artifact ID which uniquely identifies the pull request. To generate an artifact ID for a pull request, use this template:
vstfs:///CodeReview/CodeReviewId/{projectId}/{pullRequestId}
Even using the value that is returned in the artifactId field on the PR using the GetById method results in a Doesn't exist or Don't have access response, so if someone else knows how to use this method and if it gives exact build ids being evaluated for the policy configurations, I'd be glad to hear it.
An alternative to get what you actually desire
It sounds like the only use you have for the branch policy is to run a "gate build" before completing the merge.
Why not create the PR with autocomplete.
Name - autoCompleteSetBy
Type - IdentityRef
Description - If set, auto-complete is enabled for this pull request and this is the identity that enabled it.
var me = new IdentityRef(); // you obviously need to populate this with real values
var prClient = connection.GetClient<GitHttpClient>();
await prClient.CreatePullRequestAsync(new GitPullRequest()
{
CreatedBy = me,
AutoCompleteSetBy = me,
Commits = new GitCommitRef[0],
SourceRefName = "feature/myFeature",
TargetRefName = "master",
Title = "Some good title for my PR"
},
"myBestRepository",
true);

Cannot change branch through code

I have a process whereby I want to change company AND branch when creating a new Journal Entry record. I have the following code to change branch, which I've obtained from Ruslan:
jegraph.FieldDefaulting.AddHandler<Batch.branchID>((s, e) =>
{
.NewValue = tgtbranch.BranchID; //Set to ID = 6...
.Cancel = true;
});
However, when checking the value of the branch (for troubleshooting purposes), the branch I come up with is incorrect. In order to check the branch I'm currently in, I'm using:
jegraph.Accessinfo.BranchID;
So, either my branch change code is not working, or the AccessInfo method is giving me the wrong information.
Is this the best way to change the branch for a new batch record, or am I not getting the correct info from AccessInfo?
The branch info from AccessInfo was incorrect. The original AddHandler event handler code is working as expected.

JGit: Is there a thread safe way to add and update files

The easy way to add or update files in JGit is like this:
git.add().addFilepattern(file).call()
But that assumes that the file exists in the Git working directory.
If I have a multi-threaded setup (using Scala and Akka), is there a way to work only on a bare repository, writing the data directly to JGit, avoiding having to first write the file in the working directory?
For getting the file, that seems to work with:
git.getRepository().open(objId).getBytes()
Is there something similar for adding or updating files?
"Add" is a high-level abstraction that places a file in the index. In a bare repository, you lack an index, so this is not a 1:1 correspondence between the functionality. Instead, you can create a file in a new commit. To do this, you would use an ObjectInserter to add objects to the repository (one per thread, please). Then you would:
Add the contents of the file to the repository, as a blob, by inserting its bytes (or providing an InputStream).
Create a tree that includes the new file, by using a TreeFormatter.
Create a commit that points to the tree, by using a CommitBuilder.
For example, to create a new commit (with no parents) that contains only your file:
ObjectInserter repoInserter = repository.newObjectInserter();
ObjectId blobId;
try
{
// Add a blob to the repository
ObjectId blobId = repoInserter.insert(OBJ_BLOB, "Hello World!\n".getBytes());
// Create a tree that contains the blob as file "hello.txt"
TreeFormatter treeFormatter = new TreeFormatter();
treeFormatter.append("hello.txt", FileMode.TYPE_FILE, blobId);
ObjectId treeId = treeFormatter.insertTo(repoInserter);
// Create a commit that contains this tree
CommitBuilder commit = new CommitBuilder();
PersonIdent ident = new PersonIdent("Me", "me#example.com");
commit.setCommitter(ident);
commit.setAuthor(ident);
commit.setMessage("This is a new commit!");
commit.setTreeId(treeId);
ObjectId commitId = repositoryInserter.insert(commit);
repoInserter.flush();
}
finally
{
repoInserter.release();
}
Now you can git checkout the commit id returned as commitId.

How to revert changes in couchdb?

I made a mass edit to a bunch of docs in my couchdb, but I made a mistake and overwrote a field improperly. I can see that the previous revision is there. How do I revert back to it?
My current best guess, based on this:
http://guide.couchdb.org/draft/conflicts.html
...is to find the doc id and the revision id and then send a delete for that document specifying the revision I want to be gone.
curl -X DELETE $HOST/databasename/doc-id?rev=2-de0ea16f8621cbac506d23a0fbbde08a
I think that will leave the previous revision. Any better ideas out there?
I had to write some coffeescript (using underscore.js and jquery.couch) to do this. It's not a true revert, as we are getting the old revision and creating a new revision with it. Still looking for better suggestions:
_.each docsToRevert, (docToRevert) ->
$.couch.db("databaseName").openDoc docToRevert.id,
revs_info: true
,
success: (doc) ->
$.couch.db("databaseName").openDoc docToRevert.id,
rev: doc._revs_info[1].rev #1 gives us the previous revision
,
success: (previousDoc) ->
newDoc = previousDoc
newDoc._rev = doc._rev
result.save newDoc

Resources