Orphan branch in libgit2sharp - version-control

How do you create an orphan branch in libgit2sharp?
All i could find are methods which create a branch which points to a commit.
I'm looking for an effect similar to the command:
git checkout --orphan BRANCH_NAME

git checkout --orphan BRANCH_NAME actually moves the HEAD to an unborn branch BRANCH_NAME without altering the working directory nor the index.
You can perform a similar operation with LibGit2Sharp by updating the target of the HEAD reference with repo.Refs.UpdateTarget() method.
The following test demonstrates this
[Fact]
public void CanCreateAnUnbornBranch()
{
string path = CloneStandardTestRepo();
using (var repo = new Repository(path))
{
// No branch named orphan
Assert.Null(repo.Branches["orphan"]);
// HEAD doesn't point to an unborn branch
Assert.False(repo.Info.IsHeadUnborn);
// Let's move the HEAD to this branch to be created
repo.Refs.UpdateTarget("HEAD", "refs/heads/orphan");
Assert.True(repo.Info.IsHeadUnborn);
// The branch still doesn't exist
Assert.Null(repo.Branches["orphan"]);
// Create a commit against HEAD
var signature = new Signature("Me", "me#there.com", DateTimeOffset.Now);
Commit c = repo.Commit("New initial root commit", signature, signature);
// Ensure this commit has no parent
Assert.Equal(0, c.Parents.Count());
// The branch now exists...
Branch orphan = repo.Branches["orphan"];
Assert.NotNull(orphan);
// ...and points to that newly created commit
Assert.Equal(c, orphan.Tip);
}
}

Related

How to get GitHub repository Default Branch name with GraphQL?

Is there a way to use GitHub's GraphQL API to retrieve the default branch name of a repository? I can build a query to get all the branch names, but none of the properties I see reference the default branch name. Some repositories I deal with have 'master' for their default, and others have 'main'.
I am aware I can use the HEAD in some commands to workaround this. Such as "HEAD:README.md" for fetching the README.md file, like in this example query below. This repository's default branch is 'main', and I only know that from manual trial and error.
query {
repository(owner: "newrelic", name: "newrelic-ruby-agent") {
object(expression: "HEAD:README.md") {
... on Blob {
text
}
}
}
}
But when I check the local cloned copy of that repository, the .git/HEAD file points to ref: refs/heads/dev. So it seems I am actually pulling the README.md file from the dev branch, and not the default branch?
So is there a nice way to get the name of the default branch for a GitHub repository using their GraphQL API?
There's a field defaultBranchRef on the repository object:
query {
repository(owner: "newrelic", name: "newrelic-ruby-agent") {
defaultBranchRef {
name
}
}
}

Is there a way to allow only the branch creator to push on it in Github?

I have a Github organization.
I created a repository and a team to work on it.
I want to restrict push to any branch to it's creator by the default.
So that no one can push to someone's else branch.
Is there any method to do that other than adding rule for every branch manually?
I tried to add rules manually, but it's time consuming cause there are many people joining the team.
Update:
I went to my organization -> teams.
Selected the team, went to members, selected a member.
Clicked on "manage access" next to one repository, clicked "edit".
Set "Base permissions" to "Write".
Set "Repository creation" to "Disabled".
Everything else is left unchecked.
I won't comment on whether or not this is a good idea.
Suffice to say I was annoyed once leading me to create the following update hook to apply to the canonical repo (note however that Github does not allow custom update hooks so you're out of luck there):
#!/usr/bin/env python
import os
import re
import sys
from pathlib import Path
from subprocess import check_output
branch = sys.argv[1]
old_commit = sys.argv[2]
new_commit = sys.argv[3]
zero = "0000000000000000000000000000000000000000"
branch_pattern = 'feature/.+'
def get_author(commit):
return str(check_output(['git', '--no-pager', 'show', '-s', "--format='%an'", commit]).strip(), 'utf-8')
def allow(message=None):
if message:
print(message)
exit(0)
def deny(message=None):
if message:
print(message)
exit(1)
# if this isn't a feature branch bail
if not re.match(branch_pattern, branch):
allow()
# if the update is a delete bail
if new_commit == zero:
allow("update: deleting branch '%s': OK" % branch)
# if this is the first commit on the branch bail
if old_commit == zero:
allow("update: creating branch '%s': OK" % branch)
branch_log = Path('.git', 'logs', 'refs', 'heads').joinpath(*branch.split(os.path.sep))
with open(branch_log, 'r') as log:
first_commit = log.readline(81).split(sep=' ', maxsplit=1)[1]
branch_author = get_author(first_commit)
new_commit_author = get_author(new_commit)
print("update: branch = '%s'; branch author = %s; commit author = %s" % (branch, branch_author, new_commit_author))
if new_commit_author == branch_author:
allow("update: commit author == branch author: OK")
else:
deny(
"update: branch author != commit author: REJECTED\n"
"update: create a branch for your changes from the tip of %s and request a pull instead"
% branch
)
Organization owners and people with admin permissions for organization-owned repositories can enforce branch restrictions so that only certain users or teams are able to push to a protected branch.
For more info, see here

JGit - Can't track newly created branch

I would like to create a branch in an existing repository, then track that branch. The create branch succeeds, the the newly created branch is still tracking master. I have tried several different solutions, but same result - branch is created, but tracks master.
First I clone the repository:
Git.cloneRepository()./*set creds*/.setURI(..).setDirectory(...).call
So far so good.
Next, build the repository from the git file that was produced by the clone.
FileRepositoryBuilder builder = new FileRepositoryBuilder();
Repository repo = builder.setGitDir(gitFile).readEnvironment().findGitDir()
.build();
At this point, I have tried both checkingOut the branch with createBranch set to true, and doing it in two steps - create, then check out. Here is the two-step method:
git.branchCreate()
.setForce(true)
.setName(branchName)
.setStartPoint("origin/master")
.call();
git.checkout()
.setName(branchName)
.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
.setStartPoint("origin/"+branchName)
.call();
Other things I have tried:
setting upstream mode to SeetupUpstreamMode.SET_UPSTREAM
setting start point to branchname on create step
not doing separate checkout, but setting creatBranch(true) on checkout
pushing between create and checkout
The result is always .git/config file that looks like:
[remote "origin"]
url = ssh://..
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
rebase = true
[branch "newbranch1"]
remote = origin
merge = refs/heads/master << TRACKING master, not newbranch1
On the branches I create with regular git (not jgit) the config file looks like:
[remote "origin"]
url = ssh:...
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
rebase = true
[branch "newbranch2"]
remote = origin
merge = refs/heads/newbranch2 << WANT THIS
Any thoughts out there on how I can make my new branch track the branch instead of master?
Using jgit-4.6.0.201612231935
I think you cannot track a non-existing branch with JGit's CreateBranchCommand.
setStartPoint() is only useful to specify where the new branch should point to initially.
However, you can directly manipulate the repository configuration with
StoredConfig config = repository.getConfig();
config.setString( "branch", "newbranch", "remote", "origin" );
config.setString( "branch", "newbranch", "merge", "refs/heads/newbranch" );
config.save();
Does that solve your problem?
Here is the final working code: (thank you Rüdiger Herrmann )
Git git = Git.cloneRepository...
git.checkout().setCreateBranch(true).setName(branchName).call();
pushCmd.setRemote( "origin" )
.setRefSpecs( new RefSpec( branchName+":"+branchName )).call();
StoredConfig config = git.getRepository().getConfig();
config.setString( "branch", branchName, "remote", "origin" );
config.setString( "branch", branchName, "merge", "refs/heads/" + branchName );
config.save();

squashing empty commit with a commit on a same branch

I have committed some code, below I have given my problem statement.
commits
abcdef5 - empty
qwerty5 - my code
Both of them are on my branch abc
How to merge both of them into 1 commit?
I just need qwerty5 in my branch.
If abcdef5 is really empty (and it the most recent commit on your local branch abc), you could simply drop it:
git reset --hard #~
No squash required there.
Then you can push your branch.
You can use the following steps to squash your empty commit:
git rebase -i HEAD~2
1. Review the commits
pick abcdef5 a
pick qwerty5 b
2. Squash commit a into b
s abcdef5 a
pick qwerty5 b
3. Edit the final commit message
# This is a combination of 2 commits.
# The first commit's message is:
message from commit a
# This is the 2nd commit message:
message from commit b
4. Push your code
git push --force
Hope this helps. :)

GitHub Api: How to get Root :tree_sha of a repository?

How do I get the Root :tree_sha of a GitHub repository via the GitHub API?
The GitHib API help pages don't seem to explain this critical piece of information:
http://develop.github.com/p/object.html
Can get the contents of a tree by tree
SHA
tree/show/:user/:repo/:tree_sha
To get a listing of the root tree for
the facebox project from our commit
listing, we can call this:
$ curl
http://github.com/api/v2/yaml/tree/show/defunkt/facebox/a47803c9ba26213ff194f042ab686a7749b17476
Each commit contains the sha of the entire tree as of that commit.
Use the API to get a JSON object representing the master branch.
https://api.github.com/repos/:owner/:repo/branches/master
That branch's last commit includes the tree's sha that I think you're asking for.
This bit of code demonstrates how to get the head_tree_sha in Python.
import requests
token = '0...f'
key = {'Authorization':'token '+token}
master = requests.get('https://api.github.com/repos/'+owner+'/' + repo '/branches/master', headers=key)
master = master.json()
head_tree_sha = master['commit']['commit']['tree']['sha']
https://developer.github.com/v3/git/commits/
http://develop.github.com/p/commits.html
The commit tells you its tree sha.
[EDIT]
If you want the tree sha of a subfolder cd into the parent folder of the one you're interested in and run:
git ls-tree HEAD
If you want Root tree sha:
git show HEAD --format=raw
1st line has commit sha
2nd line has tree sha
I'm not sure about the GitHub API — however if you want just the hash you can use this command in your clone:
git show HEAD --format=%T | head -1
Or use %t for the abbreviated hash.