Skip to content

Commit

Permalink
Add repo.currentBranch() method
Browse files Browse the repository at this point in the history
  • Loading branch information
lahmatiy committed Oct 31, 2024
1 parent a74444b commit ce4cede
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## next

- Added `repo.currentBranch()` method
- Added `repo.describeRef(ref)` method, which returns an information object about the reference
- Added `repo.isOid(value)` method to check if a value is an object ID

Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ The algorithm to identify a default branch name:
- `main`
- `master`

#### repo.currentBranch()

Returns the current branch name along with its commit oid.
If the repository is in a detached HEAD state, `name` will be `null`.

```js
const currentBranch = repo.currentBranch();
// { name: 'main', oid: '8bb6e23769902199e39ab70f2441841712cbdd62' }

const detachedHead = repo.currentBranch();
// { name: null, oid: '8bb6e23769902199e39ab70f2441841712cbdd62' }
```

#### repo.isRefExists(ref)

Checks if a `ref` exists.
Expand Down
4 changes: 4 additions & 0 deletions fixtures/base/collect-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { fileURLToPath } from 'url';
import { createGitReader, parseTree } from '../../lib/index.js';

const repo = await createGitReader(fileURLToPath(new URL('_git', import.meta.url)));
const defaultBranch = await repo.defaultBranch();
const currentBranch = await repo.currentBranch();
const commits = await repo.log();
const stat = await repo.stat();
const objects = {};
Expand Down Expand Up @@ -38,6 +40,8 @@ fs.writeFileSync(
fileURLToPath(new URL('data.json', import.meta.url)),
JSON.stringify(
{
defaultBranch,
currentBranch,
commits,
filesLists,
filesDelta,
Expand Down
5 changes: 5 additions & 0 deletions fixtures/base/data.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"defaultBranch": "main",
"currentBranch": {
"name": "test",
"oid": "2dbee47a8d4f8d39e1168fad951b703ee05614d6"
},
"commits": [
{
"oid": "2dbee47a8d4f8d39e1168fad951b703ee05614d6",
Expand Down
2 changes: 1 addition & 1 deletion fixtures/clean/_git/HEAD
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ref: refs/heads/development
ref: refs/heads/empty-branch
56 changes: 33 additions & 23 deletions src/resolve-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ export async function createRefIndex(gitdir: string) {
}

const refInfo = await refResolver.resolve(expandedRef);

const [, scope, path] = expandedRef.match(/^([^/]+\/[^/]+)\/(.+)$/) || [
'',
'refs/heads',
Expand Down Expand Up @@ -163,6 +162,37 @@ export async function createRefIndex(gitdir: string) {
.replace(/^ref:\s*/, '')
);

const currentBranch = async function () {
const { ref, oid } = await describeRef('HEAD');
const name = ref ? (await describeRef(ref)).name : null;

return {
name,
oid
};
};

// inspired by https://usethis.r-lib.org/reference/git-default-branch.html
const defaultBranch = async function () {
const branches = (await listBranches()) as string[]; // FIXME: remove string[]

if (branches.length <= 1) {
return basename(branches[0]);
}

const branchRef =
expandRef('refs/remotes/upstream/HEAD') ||
expandRef('refs/remotes/origin/HEAD') ||
expandRef('refs/heads/main') ||
expandRef('refs/heads/master');

if (branchRef) {
return branchRef.endsWith('/HEAD') ? readRefContent(branchRef) : basename(branchRef);
}

return null;
};

return {
isOid,
isRefExists: (ref: string) => expandRef(ref) !== null,
Expand All @@ -175,28 +205,8 @@ export async function createRefIndex(gitdir: string) {
listBranches,
listTags,

// inspired by https://usethis.r-lib.org/reference/git-default-branch.html
async defaultBranch() {
const branches = (await listBranches()) as string[]; // FIXME: remove string[]

if (branches.length === 1) {
return basename(branches[0]);
}

const branchRef =
expandRef('refs/remotes/upstream/HEAD') ||
expandRef('refs/remotes/origin/HEAD') ||
expandRef('refs/heads/main') ||
expandRef('refs/heads/master');

if (branchRef) {
return branchRef.endsWith('/HEAD')
? readRefContent(branchRef)
: basename(branchRef);
}

return null;
},
defaultBranch,
currentBranch,

async stat() {
const remotes = listRemotes();
Expand Down
25 changes: 24 additions & 1 deletion test/resolve-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ describe('resolve-ref', () => {
describe('defaultBranch()', () => {
const defaultBranches = {
base: 'main',
detached: 'main',
cruft: 'main',
'no-remotes': 'main',
upstream: 'fork-main',
clean: 'development' // FIXME
clean: 'empty-branch' // FIXME
};

for (const [repoName, expected] of Object.entries(defaultBranches)) {
Expand All @@ -34,6 +35,28 @@ describe('resolve-ref', () => {
}
});

describe('currentBranch()', () => {
const defaultBranches = {
base: { name: 'test', oid: '2dbee47a8d4f8d39e1168fad951b703ee05614d6' },
detached: { name: null, oid: '2dbee47a8d4f8d39e1168fad951b703ee05614d6' },
cruft: { name: 'main', oid: '7b84f676f2fbea2a3c6d83924fa63059c7bdfbe2' },
'no-remotes': { name: 'main', oid: '293e1ffaf7158c249fc654aec07eca555a25de09' },
upstream: { name: 'fork-main', oid: '293e1ffaf7158c249fc654aec07eca555a25de09' },
clean: { name: 'empty-branch', oid: null } // FIXME
};

for (const [repoName, expected] of Object.entries(defaultBranches)) {
(repoName === 'clean' ? it.skip : it)(repoName, async () => {
const repo = await fixtures[repoName].repo();
const actual = await repo.currentBranch();

assert.deepStrictEqual(actual, expected);

return repo.dispose();
});
}
});

describe('listBranches()', () => {
it('local branches', async () => {
const actual = await repo.listBranches();
Expand Down

0 comments on commit ce4cede

Please sign in to comment.