Skip to content

Commit 8df7330

Browse files
committed
feat: add support for pushing to empty remote repositories during migration
1 parent 08dc5cb commit 8df7330

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed

src/operator/git-repository.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,42 @@ export class GitRepository {
9090
}
9191
}
9292

93+
/**
94+
* Checks if the remote repository is empty (has no branches).
95+
* This is useful for migration scenarios where we want to push to an empty external repo.
96+
*/
97+
private async isRemoteEmpty(): Promise<boolean> {
98+
try {
99+
// git ls-remote --heads returns nothing if no branches exist
100+
const result = await this.git.listRemote(['--heads', 'origin'])
101+
const isEmpty = result.trim() === ''
102+
if (isEmpty) {
103+
this.d.info('Remote repository is empty (no branches found)')
104+
}
105+
return isEmpty
106+
} catch (error) {
107+
// If ls-remote fails, assume remote is not empty to avoid accidental pushes
108+
this.d.warn('Failed to check if remote is empty, assuming not empty:', getErrorMessage(error))
109+
return false
110+
}
111+
}
112+
113+
/**
114+
* Pushes local content to an empty remote repository.
115+
* Used during migration from internal Gitea to external Git.
116+
*/
117+
private async pushToEmptyRemote(): Promise<void> {
118+
try {
119+
this.d.info('Pushing local content to empty remote repository...')
120+
// Push current branch (main) to origin, setting upstream
121+
await this.git.push(['--set-upstream', 'origin', 'main'])
122+
this.d.info('Successfully pushed local content to remote repository')
123+
} catch (error) {
124+
this.d.error('Failed to push to empty remote:', getErrorMessage(error))
125+
throw new OperatorError('Push to empty remote failed', error as Error)
126+
}
127+
}
128+
93129
private async getChangedFiles(fromRevision: string, toRevision: string): Promise<string[]> {
94130
const diffResult = await this.git.diff([`${fromRevision}..${toRevision}`, '--name-only'])
95131
return diffResult.split('\n').filter((file) => file.trim().length > 0)
@@ -131,6 +167,21 @@ export class GitRepository {
131167

132168
async syncAndAnalyzeChanges(): Promise<{ hasChangesToApply: boolean; applyTeamsOnly: boolean }> {
133169
try {
170+
// Check if remote is empty (migration to new external repo scenario)
171+
// If empty, push local content to it first
172+
const remoteIsEmpty = await this.isRemoteEmpty()
173+
if (remoteIsEmpty) {
174+
this.d.info('Detected empty remote repository - pushing local content for migration')
175+
await this.pushToEmptyRemote()
176+
// After pushing, the remote now has our content, continue with normal flow
177+
// Return no changes since we just pushed our current state
178+
this._lastRevision = await this.getCurrentRevision()
179+
return {
180+
hasChangesToApply: false,
181+
applyTeamsOnly: false,
182+
}
183+
}
184+
134185
const previousRevision = this._lastRevision
135186

136187
const newRevision = await this.pull()

0 commit comments

Comments
 (0)