or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced.mdauth.mdconfig.mddiff.mdindex.mdobjects.mdreferences.mdremotes.mdrepository.mdstaging.md

staging.mddocs/

0

# Index and Staging

1

2

Git index (staging area) operations for preparing commits. The index serves as a staging area between the working directory and repository, allowing fine-grained control over what changes are included in commits and providing conflict resolution capabilities.

3

4

## Capabilities

5

6

### Index Management

7

8

The Index class provides access to the Git staging area with methods for adding, removing, and querying staged content.

9

10

```python { .api }

11

class Index:

12

def read(self, force: bool = True):

13

"""Read index from disk"""

14

15

def write(self):

16

"""Write index to disk"""

17

18

def write_tree(self, repo: Repository = None) -> Oid:

19

"""

20

Write index as tree object.

21

22

Parameters:

23

- repo: Repository (uses index's repo if None)

24

25

Returns:

26

Tree object ID

27

"""

28

29

def read_tree(self, tree: Tree):

30

"""Read tree into index"""

31

32

def clear(self):

33

"""Remove all entries from index"""

34

35

def __len__(self) -> int:

36

"""Number of entries in index"""

37

38

def __getitem__(self, key: str | int) -> IndexEntry:

39

"""Get entry by path or index"""

40

41

def __contains__(self, path: str) -> bool:

42

"""Check if path is in index"""

43

44

def __iter__(self):

45

"""Iterate over index entries"""

46

47

# File Operations

48

def add(self, path: str):

49

"""Add file to index"""

50

51

def add_all(self, pathspecs: list[str] = None, flags: int = 0):

52

"""

53

Add all matching files to index.

54

55

Parameters:

56

- pathspecs: Path patterns (None = all files)

57

- flags: Add flags

58

"""

59

60

def remove(self, path: str, stage: int = 0):

61

"""Remove file from index"""

62

63

def remove_all(self, pathspecs: list[str], flags: int = 0):

64

"""Remove all matching files from index"""

65

66

def update_all(self, pathspecs: list[str] = None, flags: int = 0):

67

"""Update all matching files in index"""

68

69

# Entry Management

70

def get_entry_stage(self, path: str, stage: int) -> IndexEntry | None:

71

"""Get entry at specific stage"""

72

73

def add_frombuffer(self, path: str, buffer: bytes, mode: int):

74

"""Add file from buffer"""

75

76

# Conflict Resolution

77

@property

78

def conflicts(self) -> ConflictCollection:

79

"""Access merge conflicts"""

80

81

def conflict_cleanup(self):

82

"""Remove conflict markers from index"""

83

84

# Status

85

def diff_to_workdir(self, **kwargs) -> Diff:

86

"""Compare index to working directory"""

87

88

def diff_to_tree(self, tree: Tree, **kwargs) -> Diff:

89

"""Compare index to tree"""

90

91

# Index Add Flags

92

GIT_INDEX_ADD_DEFAULT: int # Default behavior

93

GIT_INDEX_ADD_FORCE: int # Add ignored files

94

GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH: int # Disable pathspec matching

95

GIT_INDEX_ADD_CHECK_PATHSPEC: int # Check pathspec validity

96

```

97

98

### Index Entries

99

100

IndexEntry represents a single file in the Git index with metadata.

101

102

```python { .api }

103

class IndexEntry:

104

@property

105

def path(self) -> str:

106

"""File path relative to repository root"""

107

108

@property

109

def oid(self) -> Oid:

110

"""Object ID of file content"""

111

112

@property

113

def mode(self) -> int:

114

"""File mode (permissions)"""

115

116

@property

117

def flags(self) -> int:

118

"""Entry flags"""

119

120

@property

121

def flags_extended(self) -> int:

122

"""Extended entry flags"""

123

124

@property

125

def stage(self) -> int:

126

"""Merge stage (0=normal, 1=base, 2=ours, 3=theirs)"""

127

128

@property

129

def ctime(self) -> int:

130

"""Creation time"""

131

132

@property

133

def mtime(self) -> int:

134

"""Modification time"""

135

136

@property

137

def dev(self) -> int:

138

"""Device ID"""

139

140

@property

141

def ino(self) -> int:

142

"""Inode number"""

143

144

@property

145

def uid(self) -> int:

146

"""User ID"""

147

148

@property

149

def gid(self) -> int:

150

"""Group ID"""

151

152

@property

153

def file_size(self) -> int:

154

"""File size in bytes"""

155

```

156

157

### Conflict Resolution

158

159

Handle merge conflicts in the index with three-way merge information.

160

161

```python { .api }

162

class ConflictCollection:

163

def __getitem__(self, path: str) -> tuple[IndexEntry, IndexEntry, IndexEntry]:

164

"""Get conflict entries (ancestor, ours, theirs)"""

165

166

def __contains__(self, path: str) -> bool:

167

"""Check if path has conflicts"""

168

169

def __iter__(self):

170

"""Iterate over conflicted paths"""

171

172

def __len__(self) -> int:

173

"""Number of conflicted files"""

174

175

def get(self, path: str) -> tuple[IndexEntry, IndexEntry, IndexEntry] | None:

176

"""Get conflict entries with None fallback"""

177

178

class MergeFileResult:

179

@property

180

def automergeable(self) -> bool:

181

"""True if merge was automatic"""

182

183

@property

184

def path(self) -> str:

185

"""Merged file path"""

186

187

@property

188

def mode(self) -> int:

189

"""Merged file mode"""

190

191

@property

192

def contents(self) -> bytes:

193

"""Merged file contents"""

194

```

195

196

### Status Constants

197

198

File status flags used throughout pygit2 for working directory and index state.

199

200

```python { .api }

201

# Index Status Flags

202

GIT_STATUS_CURRENT: int # No changes

203

GIT_STATUS_INDEX_NEW: int # New file in index

204

GIT_STATUS_INDEX_MODIFIED: int # Modified file in index

205

GIT_STATUS_INDEX_DELETED: int # Deleted file in index

206

GIT_STATUS_INDEX_RENAMED: int # Renamed file in index

207

GIT_STATUS_INDEX_TYPECHANGE: int # Type change in index

208

209

# Working Tree Status Flags

210

GIT_STATUS_WT_NEW: int # New file in workdir

211

GIT_STATUS_WT_MODIFIED: int # Modified file in workdir

212

GIT_STATUS_WT_DELETED: int # Deleted file in workdir

213

GIT_STATUS_WT_TYPECHANGE: int # Type change in workdir

214

GIT_STATUS_WT_RENAMED: int # Renamed file in workdir

215

GIT_STATUS_WT_UNREADABLE: int # Unreadable file

216

217

# Combined Status Flags

218

GIT_STATUS_IGNORED: int # Ignored file

219

GIT_STATUS_CONFLICTED: int # Conflicted file

220

```

221

222

### Usage Examples

223

224

#### Basic Index Operations

225

226

```python

227

import pygit2

228

229

repo = pygit2.Repository('/path/to/repo')

230

index = repo.index

231

232

# Check index status

233

print(f"Index has {len(index)} entries")

234

235

# Add single file

236

index.add('README.md')

237

index.write()

238

239

# Add all files

240

index.add_all()

241

index.write()

242

243

# Remove file

244

index.remove('unwanted.txt')

245

index.write()

246

247

# Stage specific files

248

pathspecs = ['*.py', 'docs/*.md']

249

index.add_all(pathspecs)

250

index.write()

251

```

252

253

#### Working with Index Entries

254

255

```python

256

# Examine index contents

257

for entry in index:

258

print(f"{entry.path}: {entry.oid} (mode: {oct(entry.mode)})")

259

if entry.stage > 0:

260

print(f" Merge stage: {entry.stage}")

261

262

# Get specific entry

263

if 'main.py' in index:

264

entry = index['main.py']

265

print(f"main.py: {entry.oid}")

266

print(f"Size: {entry.file_size} bytes")

267

print(f"Modified: {entry.mtime}")

268

269

# Check for conflicts

270

if index.conflicts:

271

print(f"Index has {len(index.conflicts)} conflicted files")

272

```

273

274

#### Creating Commits from Index

275

276

```python

277

# Create tree from index

278

tree_oid = index.write_tree()

279

280

# Create commit

281

signature = pygit2.Signature('Author', 'author@example.com')

282

commit_oid = repo.create_commit(

283

'HEAD',

284

signature,

285

signature,

286

'Commit message',

287

tree_oid,

288

[repo.head.target] # Parent commits

289

)

290

291

print(f"Created commit: {commit_oid}")

292

```

293

294

#### Conflict Resolution

295

296

```python

297

# Check for merge conflicts

298

if repo.index.conflicts:

299

print("Resolving conflicts:")

300

301

for path in repo.index.conflicts:

302

ancestor, ours, theirs = repo.index.conflicts[path]

303

304

print(f"\nConflict in {path}:")

305

if ancestor:

306

print(f" Ancestor: {ancestor.oid}")

307

if ours:

308

print(f" Ours: {ours.oid}")

309

if theirs:

310

print(f" Theirs: {theirs.oid}")

311

312

# Get file contents for manual merge

313

if ours and theirs:

314

our_content = repo[ours.oid].data

315

their_content = repo[theirs.oid].data

316

317

# Perform manual merge (simplified)

318

merged_content = merge_files(our_content, their_content)

319

320

# Stage resolved content

321

repo.index.add_frombuffer(

322

path,

323

merged_content,

324

pygit2.GIT_FILEMODE_BLOB

325

)

326

327

# Write resolved index

328

repo.index.write()

329

330

# Clean up conflict markers

331

repo.index.conflict_cleanup()

332

```

333

334

#### Index Diffs

335

336

```python

337

# Compare index to working directory

338

diff = repo.index.diff_to_workdir()

339

print(f"Working directory has {len(diff.deltas)} changes:")

340

341

for delta in diff.deltas:

342

status_map = {

343

pygit2.GIT_DELTA_ADDED: "Added",

344

pygit2.GIT_DELTA_DELETED: "Deleted",

345

pygit2.GIT_DELTA_MODIFIED: "Modified",

346

pygit2.GIT_DELTA_RENAMED: "Renamed"

347

}

348

status = status_map.get(delta.status, "Unknown")

349

print(f" {status}: {delta.new_file.path}")

350

351

# Compare index to HEAD

352

head_tree = repo[repo.head.target].tree

353

diff = repo.index.diff_to_tree(head_tree)

354

print(f"Index has {len(diff.deltas)} staged changes")

355

```

356

357

#### Advanced Index Operations

358

359

```python

360

# Add files from buffer

361

file_content = b"print('Hello, World!')"

362

index.add_frombuffer('hello.py', file_content, pygit2.GIT_FILEMODE_BLOB)

363

364

# Update only modified files (don't add new files)

365

index.update_all()

366

367

# Force add ignored files

368

index.add_all(flags=pygit2.GIT_INDEX_ADD_FORCE)

369

370

# Read specific tree into index

371

tree = repo.revparse_single('v1.0.0').tree

372

index.read_tree(tree)

373

index.write()

374

375

# Clear index completely

376

index.clear()

377

index.write()

378

```