Documentation
¶
Overview ¶
Package porcelain parses `git status --porcelain -z` output.
It exists because four packages need to read the same wire format and none of them may depend on the others: pkg/repository owns the Status projection, pkg/branch and pkg/doctor hold only a *gitcmd.Executor, and pkg/reposync is under a standing rule not to call pkg/repository from its executor. Every previous attempt to satisfy that produced another hand-rolled line splitter, and each one rediscovered the same defects — collapsed untracked directories, C-quoted paths that name no real file, renames flattened into a single nonexistent path.
The package deliberately stops at the record level. Projecting records onto a domain type is the caller's business, and the two projections that exist disagree: Status folds the XY pair into a union of file lists, while a conflict check needs the pair intact. Sharing the projection would force one of them to lose information; sharing the parse costs nothing.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsUnmerged ¶
IsUnmerged reports whether an XY code marks a path with unresolved conflicts.
There are seven such codes and only five contain a U:
DD both deleted AU added by us UD deleted by them UA added by them DU deleted by us AA both added UU both modified
Testing for U alone is the recurring bug — it silently passes AA and DD, the two shapes a conflicted rename or a delete/delete merge produces, so a repository mid-conflict reads as merely dirty.
Callers may hold a code from an arbitrary source, so a short string is answered false rather than indexed.
Types ¶
type Record ¶
Record is one entry of `git status --porcelain -z`.
Code is kept as the raw two-character XY pair rather than a normalized single letter, because the two sides answer different questions: X is what the index holds against HEAD, Y is what the working tree holds against the index. A caller that only needs "what happened to this path" can collapse the pair, but one that has to separate staged from unstaged changes cannot recover it once collapsed.
func Parse ¶
Parse splits `git status --porcelain -z` output into records.
Three properties make -z mandatory rather than a preference:
- Without it, git C-quotes any path containing a space or (under core.quotePath) a non-ASCII byte, so the string emitted names no real file. -z disables quoting entirely.
- Records are NUL-terminated, so a path may itself contain a newline without splitting one entry into two.
- Because records are not newline-delimited, nothing may be trimmed. The leading space in " M file" is the load-bearing distinction between "index unchanged" and "index modified"; trimming it reclassifies an unstaged edit as a staged one and shifts the path by one byte. gitcmd.Executor.RunOutput trims its result, so callers must read stdout untrimmed — Run, not RunOutput.
Pair -uall with it at the call site to stop git from collapsing an untracked directory into a single `dir/` entry, which would report N new files as one.
Anything that is not a well-formed record is an error rather than a skip. The only input this function may discard is the empty string, which -z produces by construction: it terminates every record with a NUL, so the final split always yields one trailing empty field. A non-empty record shorter than "XY P" is not something git emits, so it means either that the format is not what this parser believes it to be or that stdout was truncated — and that is the one signal worth keeping, not the one worth swallowing.