Combining and exchanging work
The previous pages cover reading and recording history. This page covers combining it, transforming files on the way in and out, and attaching extra meaning to commits. girt doesn’t cover most of these yet.
Merging
Section titled “Merging”A merge combines two lines of work. Git finds their merge base, diffs each side against it, and keeps both sets of changes. A file changed on only one side takes that side’s version. Where both sides changed overlapping regions differently, Git records a conflict. Adjacent changes can also be grouped into one conflict. The index holds the available base, ours and theirs versions in stages 1–3; a missing version has no entry at its stage. Text conflicts put markers in the working file:
<<<<<<< HEADlet limit = 10;=======let limit = 20;>>>>>>> featureBinary conflicts keep our version in the working file without text markers; the index retains the available original sides for resolution.
Whitespace options change which lines compare equal without rewriting accepted payloads. Attributes can select text, binary, union or custom merge drivers for a path. Marker presentation is separate from the original versions retained in the index.
When there are several merge bases, Git’s default strategy first merges the bases together into a virtual base. Cherry-pick, revert, rebase and stash are all merges underneath: cherry-picking C is a merge whose base is C’s parent. Without merging, a tool can read history but can’t combine work.
girtPartial Content and whitespace policies, rename-aware tree/commit merging with clean recursive bases,
original conflict stages, per-path attribute/custom drivers and guarded conflict checkout. Conflicted virtual
bases, directory-stage expansion and ambiguous rename layouts remain limited.
Remaining work is tracked in #493.
Cherry-pick, revert, rebase and stash
Section titled “Cherry-pick, revert, rebase and stash”Cherry-pick applies a commit’s change to a new destination. The selected parent supplies the base, the destination supplies ours, and the picked commit supplies theirs. Revert swaps the commit and its parent to apply the inverse change. A merge commit needs a mainline parent so Git knows which change to replay. Rebase repeats cherry-picking onto each newly created destination commit.
Stash records working files and the index as commits, with an optional extra parent for untracked files. Applying a stash merges those saved changes into the current checkout. These operations can stop on the same conflicts as a merge; the provisional tree alone does not resolve them.
girtPartial Object-only cherry-pick, revert and ordered rebase sequences with mainline selection, commit metadata,
explicit empty policies and first-conflict progress. Linear todo execution and explicit Git sequencer
recovery metadata are supported. Stash snapshots, apply/index plans and files-backend stash history
are supported through explicit publication; merge-topology replay, repository stash capture/cleanup
and reftable stash history editing remain partial.
Remaining work is tracked in #608.
Filters
Section titled “Filters”Filters transform file contents between the repository and the working tree:
- Line endings. With
core.autocrlfor thetextandeolattributes, Git stores LF in the repository and writes CRLF on Windows checkouts. identexpands$Id$to$Id: <blob id> $on checkout and collapses it again when adding.- Clean and smudge drivers (
filter.<name>.cleanand.smudge) run an external program: clean when adding a file, smudge when checking it out. Git’s long-running filter process protocol keeps one program running for many files.
Git LFS works this way: the repository stores a small pointer file, and the smudge filter replaces it with the real file on checkout. Without filters, checkout writes the pointer instead of the file, and status reports converted files as changed.
girtPartialLine endings, ident, clean/smudge commands and the long-running filter process (Git LFS works) in girt::filter, which runs commands only when the caller allows it. working-tree-encoding for UTF-16 and UTF-32 only (#593); no delay capability. Status and checkout apply them through Repository::status_with_filters and Repository::checkout_tree_with_filters, and clone, Repo and the CLI apply them by default.API docs
Applying patches
Section titled “Applying patches”Applying a patch takes a unified diff and changes files to
match: git apply edits the working tree or index, checking that each hunk’s context lines still
match. git format-patch turns commits into mail-ready patch files, and git am parses a mailbox
and recreates the commits with their authors and messages. Patch-based review, moving changes
between repositories, and tools that edit code by generating diffs all depend on applying them.
girtPartialBounded unified text parsing, exact-position byte-content application and in-memory tree composition with explicit object installation in girt::apply. Mode changes, new/deleted files, renames/copies and Git binary literal/delta patches are supported, including reverse application. Index drafts are computed for explicit publication through the index lock API. Combined diffs, working-file publication and mail workflows remain unsupported.issue #506
Submodules
Section titled “Submodules”A submodule embeds another repository at a fixed commit. The parent’s
tree has a gitlink entry (mode 160000) holding a
commit ID from the other repository, and .gitmodules says where to fetch it:
[submodule "vendor/lib"] path = vendor/lib url = https://example.com/lib.gitgit submodule update --init clones the submodule and checks out the recorded commit. Status
reports whether a submodule’s checkout matches the recorded commit.
girtPartialRead and lexically validate .gitmodules from explicit worktree/index/commit sources and resolve relative URLs with girt::submodule. Inspect gitlink registration, HEAD and nested dirtiness with ignore precedence; configured parent status includes visible changes. Compute initialization assignments and recorded-commit update plans without side effects. Init/update execution and clone recursion remain unsupported.issue #507
Notes attach text to a commit after it was made, without changing its ID. They live in an
ordinary commit history under refs/notes/commits, whose trees map commit IDs to note blobs, split
into fanout directories (ab/cdef…) once there are many. Review sign-offs, CI results and links to
review pages are stored this way.
girtPartialRead arbitrary fanout; compose and store flat notes histories. Fanout writes, merges and automatic rewrite propagation remain.issue #508
Mailmap
Section titled “Mailmap”.mailmap (or the file in mailmap.file, or the blob in mailmap.blob) maps old or inconsistent
names and emails to one canonical identity:
Jane Doe <[email protected]> <[email protected]>git log, git blame and git shortlog use it, so one person doesn’t appear as several.
girtPartialIdentity mapping and configured sources; home-prefixed paths require caller expansion.issue #509
Signatures
Section titled “Signatures”Commits and tags can carry a signature proving who made them, stored as a gpgsig header in the
commit or appended to the tag message. Signing calls gpg, or ssh-keygen -Y sign when
gpg.format=ssh. Verifying (git verify-commit, git log --show-signature) checks the signature
against trusted keys: the GPG keyring, or for SSH the allowed signers file in
gpg.ssh.allowedSignersFile. Forges and security-conscious tools need verification.
girtSupportedSigning and verifying commits and tags with gpg, gpgsm and ssh-keygen, configured as Git configures them.issue #510
Describe
Section titled “Describe”git describe names a commit by the nearest earlier tag: v1.2.0-14-gabc1234 means 14 commits
after v1.2.0, at commit abc1234. Build tools use it for version strings.
girtPartialRepository::describe with tag kinds, patterns, candidates and first-parent; counts are exact where Git estimates. No --dirty, --broken or --contains yet.issue #511
Pathspecs
Section titled “Pathspecs”A pathspec is the mini-language commands use to choose paths: src/*.rs, ':(exclude)tests',
':(icase)readme', and ':/' for the repository root from anywhere inside it. Status, diff, log,
add and checkout all take pathspecs, so they need one shared implementation that matches identically
everywhere.
A spec without wildcards names a path and everything under it on component boundaries, so src
selects src/a.rs but sr selects nothing. Without :(glob) magic, * matches / too: *.rs
selects src/sub/b.rs. With it, * stops at / and ** spans directories.
git log -- <pathspec> shows the commits that change a selected path. A merge that left those paths
exactly as one of its parents had them is skipped, and the walk follows only that parent, so side
branches that never touched the paths are not listed.
girtPartialParsing and matching are in girt::pathspec; status, tree diff and log take pathspecs but refuse attr: magic, and log has only Git’s default merge simplification. Remaining work is tracked in #495.API docs
Commit message cleanup and trailers
Section titled “Commit message cleanup and trailers”Before committing, Git cleans up the message according to commit.cleanup. By default, when the
message was edited in an editor, it strips lines starting with #, trailing whitespace, and leading
and trailing blank lines, and collapses runs of blank lines; a message given with -m keeps #
lines. Trailers are the Key: value lines at the end of a message, such as Signed-off-by: and
Co-authored-by:; git interpret-trailers parses and adds them. Both are pure text functions that
commit tools need.
girtPartialCleanup and trailers are in girt::message; trailer.<name>.command is refused.issue #512
Ahead/behind counts
Section titled “Ahead/behind counts”“3 ahead, 5 behind origin/main” counts the commits each side has that the other doesn’t, measured
from their merge base
(git rev-list --left-right --count main...origin/main). Every status bar and branch list shows it.
Counted naively, it walks both histories; a
commit-graph lets the walk stop early.
girtSupportedObjects::ahead_behind stops early with a commit-graph; without one it reads both full histories.issue #513
Reset modes
Section titled “Reset modes”git reset moves the current branch, at one of three depths: --soft moves the branch only;
--mixed (the default) also resets the index; --hard also resets the working files.
git reset -- path resets one path’s index entry, which is how a file gets unstaged. Each is a
combination of ref updates, index
writes and checkout, done in a safe order.
girtPartialSoft and mixed resets and path resets in girt::reset, with ORIG_HEAD and reflog messages as Git writes them; hard, keep and merge resets are not yet supported.issue #514
Pluggable backends
Section titled “Pluggable backends”Git itself assumes objects live in .git/objects/ and refs in .git/refs/ or reftable. Libraries
can instead let callers supply storage: an in-memory repository for tests, a database or object
store on a server, or another tool’s own storage. Every read path goes through that interface, so it
is a design decision before it is code.
girtNot yetissue #515
Transport hooks
Section titled “Transport hooks”Applications often need to customise remote access: handle a custom URL scheme, inspect a TLS
certificate or SSH host key and decide whether to accept it, route through a proxy, or supply
credentials their own way. Git does this with remote-<scheme> helper programs, http.proxy,
http.sslCAInfo and SSH configuration; a library needs callbacks for the same decisions. Enterprise
networks, custom authentication and tests depend on them.
girtPartialHTTP proxies, client certificates, http.extraHeader and http.userAgent are configurable; no custom URL schemes or certificate and host-key callbacks.issue #516