# Review Git changes, stage selected lines and resolve a source conflict

Keep buffer, disk, index and HEAD distinct; review staged replacements, recover local index actions and resolve a conflict without confusing syntax review with simulation evidence.

## Compatibility

2.9.0 release target; current Windows Git controls and read-only C API checks

Published guidance with release-applicability limits. Confirm package applicability before following this guide.

[Download Complete disposable Git repositories and read-only agent client (ZIP)](/_functions/kbExample?name=josim-git.zip)

Includes: circuit.cir, prepare_git.py, inspect_git.py, README.txt.

## Keep four versions distinct

Git review helps you decide which source changes to stage. It does not establish whether a simulation succeeded. Keep the saved source revision, effective run settings, diagnostics and numerical results together when recording a result; a clean diff alone is not that evidence.

Use JoSIM®-Pro 2.9 with Git 2.49 or newer installed on the application host and available to the app. The checked Windows route used Git 2.56.0. Open a saved netlist inside a Git working tree. Untitled and read-only documents cannot change the index.

| Version | Meaning |
| --- | --- |
| Buffer | The text currently open in the editor, including unsaved changes. |
| Disk | The last explicitly saved working-tree file. |
| Index | The staged version intended for a future commit. |
| HEAD | The current committed version. Staging does not create a commit. |

## Inspect changes before choosing a write action

The inspector is modeless, so source work can continue while it is open. A separate modal Review changes... window controls writes. Read-only inspection does not pause autosave; the manual specifies that autosave pauses during the modal write review. That timing rule is source-derived, rather than independently exercised by this example.

1. Choose the Git status button or Edit > Git from the source editor. Read the two-column status: the first column describes index versus HEAD; the second describes saved disk versus index. Unsaved editor changes remain separate.
2. Check the inspector’s labelled comparison source. Filter paths and open a selected status row with Enter when useful. That can open another source tab without rebinding the inspector or its write review.
3. Select staged versus buffer, HEAD versus buffer, HEAD versus staged, or disk versus buffer. Refresh for a new snapshot. Recent path revisions can be inspected without applying them.
4. Use Git gutter markers to compare index with buffer; use the unsaved overview lane to compare disk with buffer. Read any raw-content, encoding or unavailable-object warning before interpreting the diff.

## Review a whole-file stage or unstage

1. Save or reconcile the labelled source explicitly. Preview stage disk uses the saved file; it does not save an unsaved buffer.
2. Choose Review changes..., then Preview stage disk or Preview unstage. Stage includes every saved change in this file. Unstage restores the current HEAD version in the index; it leaves the working source alone.
3. Read the current staged file and its complete proposed replacement. Acknowledge that exact replacement, then choose Apply reviewed.
4. Recheck status and the staged comparison. If disk, index, HEAD, operation metadata, configuration or attributes changed, obtain a new preview and review it again. An existing Git lock is a refusal, not permission to delete the lock.

![Actual complete proposed staged teaching file: RVAL is 20 and the saved note is included. The parameter and note changes are highlighted.](https://static.wixstatic.com/media/31de30_0156c548e2a94a889cafc8c6ad0074d9~mv2.png)

Complete proposed staged file from the teaching repository. RVAL=20 and the saved note are both present; applying this index review does not rewrite the editor or saved file.

## Choose only the intended lines

Choose lines to stage compares index with saved disk. Choose lines to unstage compares index with HEAD. Check additions and removals separately: a replacement may need both the old-line removal and new-line addition. Space toggles one line; Shift+Space or double-click toggles its contiguous hunk.

Choose Preview chosen lines, read the complete resulting staged file, acknowledge it and Apply reviewed. Unselected staged content and file mode are preserved. In the native teaching check, unstaging only the saved note retained RVAL=20 in the index; staging that note again restored the full saved version.

Partial actions require non-binary UTF-8 text and preserve raw LF/CRLF and final-newline state. They are limited to four million changed-middle comparison cells and 2048 changed lines, without a coarse fallback or fuzzy patch matching. A stale selection needs a new review.

![Actual Choose lines to unstage list: only removal of the saved-note line is checked; both parameter replacement lines remain unchecked.](https://static.wixstatic.com/media/31de30_289a82560ceb46a4858d05227ab7ffd6~mv2.png)

Only the saved-note removal is selected for unstaging. The unchecked parameter replacement remains staged. Open the actual-size image to read the full line labels.

## Recover the index separately from text Undo

Preview recovery proposes the previous staged version from the latest local action record. Review and acknowledge that proposal before applying it. Recovery changes the index; Ctrl+Z changes editor text. Neither substitutes for the other.

Recovery records retain blob bytes in jpro-index-history inside this worktree’s Git metadata. They survive an app restart and are not automatically deleted. Recovery requires the staged version and HEAD to match the recorded action; merge recovery also checks operation metadata.

The native check explicitly reopened the saved source in a second app process and recovered its prior staged version. This proves persistence of that index record, not automatic restoration of tabs or a complete GUI session. Preview preparation may create unreferenced local Git objects while leaving the real index and source untouched.

## Resolve a conflict in separate reviewed steps

Applied merge sides, original disk text and reviewed result are retained in jpro-merge-history. Reopen merge draft retrieves a preserved result only while the sides still match. Preview recovery after marking resolved restores the exact prior conflict stages without changing the saved result. Saving and index replacement are separate operations, with no atomic transaction across both.

1. Start from a saved or reconciled conflicted file. Choose Review conflict. Inspect immutable stage 1 (base), stage 2 and stage 3 alongside the original conflicted disk text and editable result. During rebase, stage 2 is upstream and stage 3 is the replayed commit.
2. Move into the marker block or choose Next conflict. Choose stage 2, stage 3 or both, then inspect every resulting statement. Use both puts stage 2 before stage 3 and can duplicate definitions. The teaching conflict changes RVAL from 10 to competing 20 and 30; both values are not an acceptable single-parameter resolution.
3. Choose Validate result. This checks UTF-8, remaining markers and non-simulating single-file syntax. Review semantic diagnostics and include dependencies separately. A syntax-valid result is not proof of circuit behaviour.
4. Review and acknowledge the full validated result, then Apply reviewed. It enters the source buffer as one undoable edit. Disk and the conflict stages remain unchanged. Ctrl+Z and Ctrl+Y were checked on that edit.
5. Choose Save merged buffer explicitly. Then Preview mark resolved, inspect the exact saved result and preserved conflict sides, acknowledge and Apply reviewed. This separate action replaces only this file’s unmerged index entries.
6. Inspect the remaining repository state in an external Git client before committing or continuing the operation. The GUI does not commit, abort or continue a merge/rebase. The teaching check deliberately leaves the other file conflicted and the merge unfinished.

![Actual editable merge-result pane after choosing stage 2: one RVAL=20 statement remains and the conflict markers are removed.](https://static.wixstatic.com/media/31de30_7d8ab3c90ae64f66a77a8f6dad9a10a5~mv2.png)

Actual result after choosing stage 2 in the synthetic conflict. The marker block is gone and RVAL=20 remains. Validation and buffer application precede the separate source save and index resolution.

## Use a disposable complete teaching example

The download contains circuit.cir, prepare_git.py and inspect_git.py. Run the builder only in a new teaching destination. It creates two independent local repositories, review and conflict, with synthetic commits and no remotes. It never discovers or commits your product repository.

The review repository has a saved RVAL=20 change and note against a committed RVAL=10 baseline, plus a separately staged notes.txt. The conflict repository deliberately creates RVAL=20 versus RVAL=30 conflicts in circuit.cir and other.cir, plus another independently staged note. Open circuit.cir in each repository to follow the GUI steps.

The builder ignores system/global Git configuration in its child commands and writes only local teaching configuration. The optional empty.gitconfig passed to the read-only client isolates that client process. These options do not reconfigure a separately launched GUI or disable policies in a real project. If the GUI reports conversion or other unsupported settings, respect the guard and use an external client.

The circuit is source-control teaching text, not a qualified numerical result. This workflow does not run a solver or configure a licence. Normal GUI startup and licensing remain the product’s usual prerequisites.

## Inspect the teaching repositories from PowerShell

Run this command from the extracted three-file folder. Select a 64-bit Windows Python, an installed Git 2.49+ and a matching JoSIM 2.9 C API with its dependent DLLs. The checked client used CPython 3.13.11. If a dependency is elsewhere, repeat --dll-directory with its existing directory.

The builder’s --git selection applies to the builder only; the API and GUI must also find a compatible git on their host PATH. Both generated JSON snapshots are placed outside the two repository worktrees so the inspection itself does not introduce an untracked result file.

```text
$ErrorActionPreference = 'Stop'
$python = 'python'
$git = 'git'
$capi = 'C:\Program Files\SUN Magnetics\JoSIM-Pro\bin\josim-pro_CAPI_AMD64.dll'
& $python -B .\prepare_git.py --git $git --results .\git-teaching
if ($LASTEXITCODE -ne 0) { throw 'The new teaching repositories were not prepared.' }
& $python -B .\inspect_git.py --capi $capi --source .\git-teaching\review\circuit.cir --git-config .\git-teaching\empty.gitconfig --output .\git-teaching\review-snapshot.json
if ($LASTEXITCODE -ne 0) { throw 'The current review snapshot is not ready.' }
& $python -B .\inspect_git.py --capi $capi --source .\git-teaching\conflict\circuit.cir --git-config .\git-teaching\empty.gitconfig --output .\git-teaching\conflict-snapshot.json
if ($LASTEXITCODE -ne 0) { throw 'The current conflict snapshot is not ready.' }
Get-Content -LiteralPath .\git-teaching\review-snapshot.json -Raw | ConvertFrom-Json | Select-Object state,git_version,entries
Get-Content -LiteralPath .\git-teaching\conflict-snapshot.json -Raw | ConvertFrom-Json | Select-Object state,conflicted,stages
```

## Interpret the agent reply and stop on unavailable state

inspect_git.py calls only JPRO_Editor_GitRead with a bounded reply buffer and no cancellation callback. It never calls GitAction, saves a source file or runs a simulation. It writes one new JSON file containing state, entries, HEAD/index blobs, conflict stages, revisions and any limitation message.

Exit 0 with state ready means the current snapshot is available. Exit 3 retains an explicit non-ready/unavailable response for inspection; exit 2 means invalid input or a loading/API failure. Existing output files are refused. Optional --revision accepts a full available 40- or 64-hex commit object ID, not HEAD or a branch expression.

Raw replies contain source bytes encoded as hex, repository paths, commit IDs and messages. Review them before sharing. They are not automatically anonymised, and a ready snapshot is neither a staged write nor numerical validation. An agent should report the distinction and request a fresh snapshot after any source or repository change.

## Respect unsupported cases and evidence limits

Each reviewed blob/result is limited to 1 MiB. Binary/non-UTF-8 sides, unavailable objects, symlinks/submodules, conflicting modes, conversion-managed files, renames and deletion resolutions need an external Git client. Index actions also refuse split/sparse indexes, special flags, filter/encoding/ident/EOL attributes, automatic EOL conversion and unsupported ref storage. Do not disable a project’s real filters or line-ending policy to force this workflow.

Twenty independent client/API checks cover complete read-only inspections, historical/unavailable revisions, unchanged source/index bytes, stale and lock refusal, guarded staging/recovery and conversion-managed-file refusal. Twenty-four native/artifact checks cover actual production controls, partial actions, merge buffer Undo/Redo, explicit save/resolve, preservation of other staged/conflicted files and recovery in a second app process.

The screenshots are unedited natural crops of the native controls, excluding path-bearing window headers at capture time. Physical menu/picker interaction, automatic GUI-session restoration, non-Windows platforms, external-editor LSP, final distributed packages and numerical runs remain outside this evidence.

## Save circuit.cir

```text
* Public source-control teaching circuit
.PARAM RVAL=10
V1 N 0 DC 1m
R1 N 0 RVAL
.TRAN 1p 20p
.PRINT V(N) I(R1)
.END
```

## Save prepare_git.py

```text
"""Create two new disposable Git repositories for the JoSIM review walkthrough.

This creates local teaching commits and a deliberate unresolved merge only in
the new destination. It never discovers or modifies an existing repository,
configures a remote, fetches, pushes or changes global Git configuration.
"""
import argparse,hashlib,json,os,re,shutil,subprocess,sys
from pathlib import Path

def sha(p):return hashlib.sha256(p.read_bytes()).hexdigest()
def write(path,text):path.write_bytes(text.encode('utf-8'))
def main():
    p=argparse.ArgumentParser(description=__doc__);p.add_argument('--git',default='git');p.add_argument('--results',type=Path,required=True);a=p.parse_args()
    try:
        binary=shutil.which(a.git)
        if not binary:raise ValueError('Select an installed Git executable, version 2.49 or newer.')
        version=subprocess.check_output([binary,'--version'],text=True).strip();match=re.match(r'git version (\d+)\.(\d+)',version)
        if not match or tuple(map(int,match.groups()))<(2,49):raise ValueError('The checked inspection contract requires Git 2.49 or newer.')
        work=a.results.absolute();source=Path(__file__).resolve().with_name('circuit.cir');original=sha(source);baseline=source.read_text(encoding='utf-8')
        if work.exists() or not work.parent.is_dir():raise ValueError('Choose a new destination under an existing parent; existing paths are never reused.')
        work.mkdir();config=work/'empty.gitconfig';write(config,'')
        env=os.environ.copy();env.update(GIT_CONFIG_NOSYSTEM='1',GIT_CONFIG_GLOBAL=str(config),GIT_TERMINAL_PROMPT='0',
          GIT_AUTHOR_DATE='2026-01-01T00:00:00+00:00',GIT_COMMITTER_DATE='2026-01-01T00:00:00+00:00')
        def git(folder,*args,allow_conflict=False):
            run=subprocess.run([binary,'-c','core.autocrlf=false','-c','core.eol=lf',*args],cwd=folder,env=env,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,timeout=30)
            if run.returncode and not (allow_conflict and run.returncode==1):raise RuntimeError('A teaching Git operation failed; no existing repository was selected.')
            return run.returncode,run.stdout.decode('utf-8',errors='replace').strip()
        def initialise(folder):
            folder.mkdir();git(folder,'init','--quiet','--initial-branch=kb-main')
            for key,value in [('user.name','Knowledge Base Example'),('user.email','kb-example'),('core.autocrlf','false'),('core.eol','lf'),('commit.gpgsign','false')]:git(folder,'config','--local',key,value)
        review=work/'review';initialise(review);write(review/'circuit.cir',baseline);write(review/'notes.txt','Public staged-note baseline\n')
        git(review,'add','--','circuit.cir','notes.txt');git(review,'commit','--quiet','-m','Public teaching baseline')
        review_head=git(review,'rev-parse','HEAD')[1]
        write(review/'circuit.cir',baseline.replace('RVAL=10','RVAL=20').replace('.END','* Reviewed saved note\n.END'))
        write(review/'notes.txt','Public separately staged note\n');git(review,'add','--','notes.txt')
        conflict=work/'conflict';initialise(conflict)
        for name in ('circuit.cir','other.cir'):write(conflict/name,baseline)
        write(conflict/'notes.txt','Public conflict-note baseline\n');git(conflict,'add','--','circuit.cir','other.cir','notes.txt');git(conflict,'commit','--quiet','-m','Public conflict baseline')
        git(conflict,'switch','--quiet','-c','kb-other')
        for name in ('circuit.cir','other.cir'):write(conflict/name,baseline.replace('RVAL=10','RVAL=30'))
        git(conflict,'commit','--quiet','-am','Public stage-three alternative')
        git(conflict,'switch','--quiet','kb-main')
        for name in ('circuit.cir','other.cir'):write(conflict/name,baseline.replace('RVAL=10','RVAL=20'))
        git(conflict,'commit','--quiet','-am','Public stage-two alternative');conflict_head=git(conflict,'rev-parse','HEAD')[1]
        code,message=git(conflict,'merge','--no-commit','--no-ff','kb-other',allow_conflict=True)
        if code!=1 or '<<<<<<<' not in (conflict/'circuit.cir').read_text(encoding='utf-8'):raise ValueError('The deliberate two-file conflict was not created.')
        write(conflict/'notes.txt','Public independently staged conflict note\n');git(conflict,'add','--','notes.txt')
        receipt={'status':'prepared','git_version':version,'baseline_sha256':original,'review_head':review_head,'conflict_head':conflict_head,
          'review_status':git(review,'status','--porcelain=v1')[1],'conflict_status':git(conflict,'status','--porcelain=v1')[1],
          'review_disk_sha256':sha(review/'circuit.cir'),'conflict_disk_sha256':sha(conflict/'circuit.cir'),
          'scope':'Only new local teaching repositories. No remote, licence, numerical run or user-repository change.'}
        if sha(source)!=original:raise ValueError('The original teaching source changed.')
        write(work/'prepared.json',json.dumps(receipt,indent=2)+'\n');print(json.dumps({'status':'prepared','repositories':['review','conflict'],'git_version':version}));return 0
    except (OSError,ValueError,RuntimeError,subprocess.TimeoutExpired) as error:print(str(error),file=sys.stderr);return 2
if __name__=='__main__':raise SystemExit(main())
```

## Save inspect_git.py

```text
"""Save a read-only JoSIM editor Git snapshot using a matching Windows C API.

The reply includes raw repository blobs, paths, commit IDs and status. Review it
before sharing. This client never calls GitAction, saves source or runs a circuit.
"""
import argparse,ctypes,json,os,re,sys
from pathlib import Path
MAX_REPLY=16*1024*1024+1
def main():
    p=argparse.ArgumentParser(description=__doc__);p.add_argument('--capi',type=Path,required=True);p.add_argument('--source',type=Path,required=True);p.add_argument('--output',type=Path,required=True)
    p.add_argument('--revision',help='Optional full available commit object ID, not a branch expression');p.add_argument('--git-config',type=Path,help='Optional existing teaching empty.gitconfig; affects this child process only');p.add_argument('--dll-directory',type=Path,action='append',default=[]);a=p.parse_args();handles=[];previous={}
    try:
        if os.name!='nt' or ctypes.sizeof(ctypes.c_void_p)!=8:raise ValueError('This checked client requires 64-bit Windows Python and a matching C API.')
        library=a.capi.resolve(strict=True);source=a.source.absolute();output=a.output.absolute()
        if not library.is_file() or source.is_symlink() or not source.is_file():raise ValueError('Select existing regular source and matching C API files; symlink sources are refused.')
        if output.exists() or not output.parent.is_dir():raise ValueError('Choose a new JSON result in an existing directory, preferably outside the inspected worktree.')
        request={'path':str(source)}
        if a.revision:
            if not re.fullmatch(r'(?:[0-9a-fA-F]{40}|[0-9a-fA-F]{64})',a.revision):raise ValueError('Revision must be a full commit object ID.')
            request['revision']=a.revision
        if a.git_config:
            config=a.git_config.resolve(strict=True)
            if not config.is_file():raise ValueError('Select an existing Git configuration file.')
            for key,value in {'GIT_CONFIG_GLOBAL':str(config),'GIT_CONFIG_NOSYSTEM':'1'}.items():previous[key]=os.environ.get(key);os.environ[key]=value
        for directory in [library.parent,*a.dll_directory]:
            directory=directory.resolve(strict=True)
            if not directory.is_dir():raise ValueError('A DLL dependency directory does not exist.')
            handles.append(os.add_dll_directory(str(directory)))
        api=ctypes.WinDLL(str(library));read=api.JPRO_Editor_GitRead;read.argtypes=[ctypes.c_char_p,ctypes.c_void_p,ctypes.c_void_p,ctypes.POINTER(ctypes.c_int),ctypes.c_void_p,ctypes.c_int];read.restype=ctypes.c_int
        raw=json.dumps(request,ensure_ascii=False).encode('utf-8')
        if len(raw)>65536:raise ValueError('The selected request exceeds the API request bound.')
        needed=ctypes.c_int();buffer=ctypes.create_string_buffer(MAX_REPLY);code=read(raw,None,None,ctypes.byref(needed),buffer,len(buffer))
        if code!=0 or not 1<=needed.value<=MAX_REPLY:raise RuntimeError('GitRead failed or exceeded the bounded reply; inspect the selected API/runtime compatibility.')
        reply=json.loads(buffer.value.decode('utf-8'))
        with output.open('x',encoding='utf-8') as stream:json.dump(reply,stream,indent=2,ensure_ascii=False);stream.write('\n')
        state=reply.get('state');print(json.dumps({'state':state,'entries':len(reply.get('entries',[])),'conflicted':reply.get('conflicted',False)}));return 0 if state=='ready' else 3
    except (OSError,ValueError,RuntimeError,AttributeError) as error:print(str(error),file=sys.stderr);return 2
    finally:
        for handle in reversed(handles):handle.close()
        for key,value in previous.items():
            if value is None:os.environ.pop(key,None)
            else:os.environ[key]=value
if __name__=='__main__':raise SystemExit(main())
```

## Sources and verification details

Recorded inputs/builds only. Complete walkthrough and final-package acceptance remain separate.

- VAL-055: Frozen maintained Windows JoSIM-Pro 2.9 candidate; Git 2.56.0. Windows Git 2.56 and matching JoSIM 2.9 editor C API, complete synthetic local repositories and read-only Python client; private owner checks exercise reviewed stage/recovery, lock/stale/configuration guards. Local fixture commits only. No product-repository change, remote, fetch/push, numerical simulation or final-package qualification Actual browser-downloaded three-input ZIP and exact PowerShell 7 article/README command with only Python and matching C API selections changed. New teaching folder with spaces, two local synthetic repositories and two read-only snapshots; no solver, licence configuration, remote, product-repository change or final-package qualification Disposable local repositories only. No numerical simulation, physical picker, automatic session restore, commit/merge continuation, remote or final-package qualification.
- VAL-056: Frozen maintained Windows JoSIM-Pro 2.9 candidate; Git 2.56.0. Frozen maintained Windows JoSIM 2.9 production Git controls and matching editor API in two normal app processes. Reviewed whole-file/partial index changes and recovery, modeless source binding, buffer-only merge with text Undo/Redo, explicit save/resolve, preserved conflict sides and new-process index recovery. Disposable local repositories only; no solver run, physical picker, automatic tab restore, merge continuation, remote or final-package qualification Disposable local repositories only. No numerical simulation, physical picker, automatic session restore, commit/merge continuation, remote or final-package qualification.

- josim-git-proposed-file: Windows current-source JoSIM 2.9 native Git scenario, reviewed 4 November 2026. Actual native teaching-file review controls; no solver result, full desktop capture or final-package qualification.
- josim-git-line-choice: Windows current-source JoSIM 2.9 native Git scenario, reviewed 4 November 2026. Actual native teaching-file review controls; no solver result, full desktop capture or final-package qualification.
- josim-git-merge-result: Windows current-source JoSIM 2.9 native Git scenario, reviewed 4 November 2026. Actual native teaching-file review controls; no solver result, full desktop capture or final-package qualification.

- josim-pro:docs/manual_source/sections/gui_source_control.tex: Git inspection, whole/partial staging, index recovery, merge review and external-client limits
- validation:josim-git-20261104: VAL-055 complete read-only Python/API and refusal checks; VAL-056 native two-process staging/merge/recovery

- 20 complete read-only client/API and 24 native/artifact checks; scoped private Windows candidate, not full package or physical operator acceptance. No solver run, remote action or product-repository change.

## Related guides

- [Find declarations, references and source locations](/knowledge-base/josim-editor-navigation)
- [Run the intended saved netlist and recognise stale results](/knowledge-base/josim-run-current-source)
- [Compare and restore a previous saved source version](/knowledge-base/josim-local-save-history)
