or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

command-execution.mdcommand-line.mdconfiguration.mdcore-generator.mdfile-system.mdgit-integration.mdindex.mdpackage-management.mdtask-lifecycle.mduser-interaction.md

git-integration.mddocs/

0

# Git Integration

1

2

Git utilities for user information and repository operations through simple-git integration with support for configuration access and GitHub username resolution.

3

4

## Capabilities

5

6

### Git User Information

7

8

Access git user configuration for author information and defaults.

9

10

```typescript { .api }

11

/**

12

* Git utilities interface

13

*/

14

interface GitUtil {

15

/**

16

* Retrieves user's name from Git in the global scope or project scope

17

* Takes what Git will use in the current context

18

* @returns Promise resolving to configured git name or undefined

19

*/

20

name(): Promise<string | undefined>;

21

22

/**

23

* Retrieves user's email from Git in the global scope or project scope

24

* Takes what Git will use in the current context

25

* @returns Promise resolving to configured git email or undefined

26

*/

27

email(): Promise<string | undefined>;

28

}

29

30

/**

31

* Git utilities instance

32

*/

33

readonly git: GitUtil;

34

```

35

36

**Usage Examples:**

37

38

```typescript

39

export default class MyGenerator extends Generator {

40

async prompting() {

41

// Get git user info for prompt defaults

42

const gitName = await this.git.name();

43

const gitEmail = await this.git.email();

44

45

this.answers = await this.prompt([

46

{

47

name: 'authorName',

48

message: 'Author name:',

49

default: gitName || 'Anonymous',

50

store: true

51

},

52

{

53

name: 'authorEmail',

54

message: 'Author email:',

55

default: gitEmail || '',

56

store: true

57

}

58

]);

59

}

60

61

async configuring() {

62

// Use git info in package.json

63

const author = {

64

name: this.answers.authorName || await this.git.name(),

65

email: this.answers.authorEmail || await this.git.email()

66

};

67

68

this.packageJson.merge({ author });

69

}

70

71

async initializing() {

72

// Validate git configuration

73

const gitName = await this.git.name();

74

const gitEmail = await this.git.email();

75

76

if (!gitName || !gitEmail) {

77

this.log('Warning: Git user.name and user.email should be configured');

78

this.log('Run: git config --global user.name "Your Name"');

79

this.log('Run: git config --global user.email "your.email@example.com"');

80

}

81

}

82

}

83

```

84

85

### Simple Git Integration

86

87

Direct access to simple-git instance for advanced git operations.

88

89

```typescript { .api }

90

/**

91

* Simple-git instance for advanced git operations

92

* Configured with current destination root and locale settings

93

*/

94

readonly simpleGit: SimpleGit;

95

```

96

97

**Usage Examples:**

98

99

```typescript

100

import { SimpleGit } from 'simple-git';

101

102

export default class MyGenerator extends Generator {

103

async writing() {

104

// Initialize git repository

105

if (this.answers.initGit && !await this.isGitRepo()) {

106

await this.simpleGit.init();

107

this.log('Initialized git repository');

108

}

109

110

// Set up git configuration for this project

111

if (this.answers.gitConfig) {

112

await this.simpleGit.addConfig('user.name', this.answers.authorName);

113

await this.simpleGit.addConfig('user.email', this.answers.authorEmail);

114

}

115

116

// Add gitignore

117

this.renderTemplate('gitignore.ejs', '.gitignore', {

118

nodeModules: true,

119

buildDirs: ['dist/', 'build/'],

120

logFiles: ['*.log', 'logs/']

121

});

122

}

123

124

async install() {

125

// Create initial commit after installation

126

if (this.answers.initialCommit) {

127

await this.simpleGit.add('.');

128

await this.simpleGit.commit('Initial commit\n\n🤖 Generated with Yeoman');

129

this.log('Created initial commit');

130

}

131

}

132

133

async end() {

134

// Set up remote repository

135

if (this.answers.remoteUrl) {

136

await this.simpleGit.addRemote('origin', this.answers.remoteUrl);

137

138

if (this.answers.pushToRemote) {

139

await this.simpleGit.push('origin', 'main');

140

this.log(`Pushed to remote: ${this.answers.remoteUrl}`);

141

}

142

}

143

}

144

145

// Helper method to check if directory is a git repo

146

async isGitRepo(): Promise<boolean> {

147

try {

148

await this.simpleGit.status();

149

return true;

150

} catch (error) {

151

return false;

152

}

153

}

154

}

155

```

156

157

### GitHub Integration (Deprecated)

158

159

GitHub utilities for username resolution (deprecated, will be removed in v8).

160

161

```typescript { .api }

162

/**

163

* @deprecated Will be removed in version 8

164

* GitHub utilities

165

*/

166

readonly github: {

167

/**

168

* @deprecated Will be removed in version 8. Use 'github-username' package with await this.git.email() result instead

169

* Retrieves GitHub's username from the GitHub API using email

170

* @returns Promise resolving to GitHub username or undefined

171

*/

172

username(): Promise<string | undefined>;

173

};

174

```

175

176

**Usage Example:**

177

178

```typescript

179

export default class MyGenerator extends Generator {

180

async prompting() {

181

// Get GitHub username (deprecated - use alternative approach)

182

let githubUsername;

183

184

try {

185

// Deprecated approach

186

githubUsername = await this.github.username();

187

} catch (error) {

188

// Alternative approach using email

189

const email = await this.git.email();

190

if (email) {

191

// Manual username derivation or use github-username package

192

githubUsername = email.split('@')[0];

193

}

194

}

195

196

this.answers = await this.prompt([

197

{

198

name: 'githubUsername',

199

message: 'GitHub username:',

200

default: githubUsername,

201

when: this.answers.publishToGithub

202

}

203

]);

204

}

205

}

206

```

207

208

### Advanced Git Operations

209

210

Complex git workflows and repository management.

211

212

**Usage Examples:**

213

214

```typescript

215

export default class MyGenerator extends Generator {

216

async initializing() {

217

// Check git status and working directory

218

if (await this.isGitRepo()) {

219

const status = await this.simpleGit.status();

220

221

if (!status.isClean()) {

222

this.log('Warning: Working directory is not clean');

223

this.log('Uncommitted changes may be overwritten');

224

225

const proceed = await this.prompt({

226

type: 'confirm',

227

name: 'continueWithDirtyRepo',

228

message: 'Continue anyway?',

229

default: false

230

});

231

232

if (!proceed.continueWithDirtyRepo) {

233

throw new Error('Aborted due to uncommitted changes');

234

}

235

}

236

}

237

}

238

239

async writing() {

240

// Create branch for generated code

241

if (this.answers.createBranch) {

242

const branchName = `generator/${this.answers.name}`;

243

244

try {

245

await this.simpleGit.checkoutLocalBranch(branchName);

246

this.log(`Created and switched to branch: ${branchName}`);

247

} catch (error) {

248

this.log(`Branch ${branchName} may already exist, switching to it`);

249

await this.simpleGit.checkout(branchName);

250

}

251

}

252

253

// Set up git hooks

254

if (this.answers.setupHooks) {

255

this.renderTemplate('pre-commit.ejs', '.git/hooks/pre-commit', {

256

runLint: this.answers.useLinting,

257

runTests: this.answers.useTesting

258

});

259

260

// Make hook executable

261

await this.spawn('chmod', ['+x', '.git/hooks/pre-commit']);

262

}

263

}

264

265

async end() {

266

// Tag the generated version

267

if (this.answers.createTag) {

268

const tagName = `v${this.packageJson.get('version')}`;

269

270

await this.simpleGit.addTag(tagName);

271

this.log(`Created tag: ${tagName}`);

272

}

273

274

// Generate changelog from commits

275

if (this.answers.generateChangelog) {

276

const log = await this.simpleGit.log(['--oneline', '--decorate']);

277

278

const changelog = log.all.map(commit =>

279

`- ${commit.message} (${commit.hash.substring(0, 7)})`

280

).join('\n');

281

282

this.writeDestination('CHANGELOG.md', `# Changelog\n\n${changelog}\n`);

283

}

284

}

285

}

286

```

287

288

### Git Configuration and Validation

289

290

Validate git setup and configure repository settings.

291

292

**Usage Examples:**

293

294

```typescript

295

export default class MyGenerator extends Generator {

296

async initializing() {

297

// Comprehensive git validation

298

await this.validateGitSetup();

299

}

300

301

async validateGitSetup() {

302

// Check if git is available

303

try {

304

await this.spawnCommand('git --version', { stdio: 'pipe' });

305

} catch (error) {

306

throw new Error('Git is not installed or not available in PATH');

307

}

308

309

// Check git configuration

310

const gitName = await this.git.name();

311

const gitEmail = await this.git.email();

312

313

if (!gitName) {

314

this.log('⚠️ Git user.name is not configured');

315

this.log('Run: git config --global user.name "Your Name"');

316

}

317

318

if (!gitEmail) {

319

this.log('⚠️ Git user.email is not configured');

320

this.log('Run: git config --global user.email "your.email@example.com"');

321

}

322

323

// Check if we're in a git repository

324

const isRepo = await this.isGitRepo();

325

if (!isRepo && this.answers.requireGitRepo) {

326

throw new Error('This generator requires a git repository. Run "git init" first.');

327

}

328

329

return { gitName, gitEmail, isRepo };

330

}

331

332

async configuring() {

333

// Configure repository-specific settings

334

if (this.answers.configureGit) {

335

// Set repository-specific configuration

336

await this.simpleGit.addConfig('core.autocrlf', 'input');

337

await this.simpleGit.addConfig('pull.rebase', 'false');

338

339

// Configure commit template

340

if (this.answers.useCommitTemplate) {

341

this.writeDestination('.gitmessage', `

342

# Title: Summary, imperative, start upper case, don't end with a period

343

# No more than 50 chars. #### 50 chars is here: #

344

345

# Remember blank line between title and body.

346

347

# Body: Explain *what* and *why* (not *how*). Include task ID (Jira issue).

348

# Wrap at 72 chars. ################################## which is here: #

349

350

351

# At the end: Include Co-authored-by for all contributors.

352

# Include at least one empty line before it. Format:

353

# Co-authored-by: name <user@users.noreply.github.com>

354

#

355

# How to Write a Git Commit Message:

356

# https://chris.beams.io/posts/git-commit/

357

#

358

# 1. Separate subject from body with a blank line

359

# 2. Limit the subject line to 50 characters

360

# 3. Capitalize the subject line

361

# 4. Do not end the subject line with a period

362

# 5. Use the imperative mood in the subject line

363

# 6. Wrap the body at 72 characters

364

# 7. Use the body to explain what and why vs. how

365

`.trim());

366

367

await this.simpleGit.addConfig('commit.template', '.gitmessage');

368

}

369

}

370

}

371

372

async isGitRepo(): Promise<boolean> {

373

try {

374

await this.simpleGit.status();

375

return true;

376

} catch (error) {

377

return false;

378

}

379

}

380

}

381

```

382

383

## Types

384

385

```typescript { .api }

386

interface GitUtil {

387

name(): Promise<string | undefined>;

388

email(): Promise<string | undefined>;

389

}

390

391

// Simple-git types (from simple-git package)

392

interface SimpleGit {

393

init(): Promise<void>;

394

status(): Promise<StatusResult>;

395

add(files: string | string[]): Promise<void>;

396

commit(message: string): Promise<CommitResult>;

397

push(remote?: string, branch?: string): Promise<void>;

398

addRemote(remoteName: string, remoteUrl: string): Promise<void>;

399

addConfig(key: string, value: string): Promise<void>;

400

getConfig(key: string): Promise<ConfigGetResult>;

401

addTag(tagName: string): Promise<void>;

402

log(options?: string[]): Promise<LogResult>;

403

checkout(branch: string): Promise<void>;

404

checkoutLocalBranch(branchName: string): Promise<void>;

405

}

406

```