Git for Mathematicians (3b): The Practice [daily work]

Published
Published .
Part 1: Preliminaries
Part 1: Preliminaries.
Part 2: The Theory
Part 2: The Theory.
Part 3a: The Practice
Part 3a: The Practice.
Part 3b: Daily Work
Part 3b: Daily Work.

Nearly five years after Part 3a, it is finally time to make our first commit. The previous post installed Git and either cloned a repository or initialized a new one. This post covers the workflow that follows: choosing what Git should track, recording changes, sharing them with coauthors, handling conflicts, and marking versions sent to arXiv or a journal.

I will continue to use the command line and GitHub in the examples, but almost everything here applies unchanged to GitLab, Bitbucket, or a self-hosted remote. Graphical Git clients expose the same underlying operations.

 Note

Run these commands from the repository’s folder. If you are unsure where you are, git status should identify the repository and current branch; git rev-parse --show-toplevel prints its root folder.

Decide what belongs in the repository

A paper repository should contain the material from which the paper is made:

  • the LaTeX source;
  • the bibliography database;
  • figures that cannot be regenerated, and the source used to generate the others;
  • code used for computations;
  • perhaps a short README.md explaining how to compile everything.

It should usually not contain temporary LaTeX files. Create a text file named .gitignore at the root of the repository, for example:

# LaTeX intermediate files
*.aux
*.blg
*.fdb_latexmk
*.fls
*.log
*.out
*.synctex.gz
*.toc

# The compiled article (adjust the name to your project)
/paper.pdf

# Editor and operating-system clutter
*~
.DS_Store

The leading slash in /paper.pdf means “the file named paper.pdf at the root of this repository”. This is safer than ignoring *.pdf, which would also ignore PDF figures that are part of the source material.

The .gitignore file itself should be committed: all collaborators then share the same rules. You can check the result with:

git status

Ignored files should no longer appear under “Untracked files”. If a file was already committed, adding it to .gitignore is not enough: ignore rules only apply to untracked files. To stop tracking a generated file without deleting your local copy, use:

git rm --cached paper.pdf
git commit -m "Stop tracking the compiled PDF"

 Warning

Do not put passwords, access tokens, or journal credentials in a repository. Deleting a secret in a later commit does not remove it from earlier history. Be equally deliberate about confidential referee material: anyone with access to the repository can retain a copy.

Make the first commit

Suppose the repository contains paper.tex, references.bib, and the .gitignore file. The following commands prepare and record the first snapshot:

git status
git add .gitignore paper.tex references.bib
git diff --staged
git commit -m "Start article"

Let us unpack this.

  1. git status reports the current branch and separates untracked, modified, and staged files. It is safe to run at any time, and doing so frequently is an excellent habit.
  2. git add copies the current contents of the named files into the staging area (the index from Part 2). Despite its name, git add is not only for new files: run it again whenever you want to stage the new contents of a modified file.
  3. git diff --staged shows exactly what the next commit would contain.
  4. git commit records that staged snapshot. The message should say what the change accomplishes, not merely which file changed.

After committing, run git status again. If nothing else has changed, Git should say that the working tree is clean. You can inspect the new history with:

git log --oneline --decorate --graph --all
git show HEAD

Here HEAD means the commit currently checked out—normally the tip of your current branch.

Connect an initialized repository to a remote

If you used git clone in Part 3a, skip this section: cloning already configured a remote.

If you used git init, create an empty repository on GitHub (or another host). When importing an existing local repository, do not ask the website to initialize it with a README, license, or .gitignore: those would create a separate initial history that first has to be reconciled.

Copy the remote URL shown by the host, then run commands of this form:

git remote add origin https://github.com/YOUR-USERNAME/riemann-hypothesis.git
git remote -v
git push -u origin main
  • origin is the conventional short name for the remote URL.
  • git remote -v lets you check that the URL is correct.
  • The first git push sends the commits and creates the main branch on the remote.
  • The -u option records origin/main as the upstream of your local main branch, so later git pull and git push need no extra arguments.

The hosting service may ask you to authenticate. For GitHub over HTTPS, an account password is not accepted: use a credential manager, GitHub CLI, or a personal access token. SSH is another option. Follow the host’s current authentication instructions rather than embedding a token in the remote URL or a shell script.

At this point the remote contains a second copy of every commit you have pushed. Remember that it contains neither uncommitted changes nor commits that you have not pushed.

The everyday cycle

For a small group writing on one shared branch, a reliable routine is:

# 1. Start from the latest shared version
git pull --ff-only

# 2. Edit and compile the paper as usual, then inspect your work
git status
git diff

# 3. Stage a coherent change and review it
git add paper.tex
git diff --staged

# 4. Record and share it
git commit -m "Clarify the proof of Proposition 3"
git push

git pull --ff-only downloads remote commits and updates your branch only when this can be done without reconciling divergent histories. At the beginning of a work session that is usually exactly what you want: either you receive your coauthors’ work cleanly, or Git stops and asks you to make a deliberate choice.

The two diff commands answer different questions:

  • git diff shows changes in your working files that have not been staged;
  • git diff --staged shows what will enter the next commit.

If a long LaTeX paragraph occupies one source line, this alternative can be easier to read:

git diff --word-diff

You do not need to commit every time you save a file. A useful commit is a coherent step that you might want to inspect, revert, or discuss later:

  • “Add proof of the compactness lemma” is useful;
  • “Apply referee’s notation throughout Section 4” is useful;
  • “Work” is not very informative;
  • mixing a rewritten proof, a bibliography cleanup, and a global reformatting in one commit makes review and conflict resolution harder.

Stage only part of a file

If you changed two unrelated passages in paper.tex, stage selected groups of lines interactively:

git add -p paper.tex

Git displays one hunk at a time and asks what to do. The most useful answers are y (stage this hunk), n (do not stage it), s (split it into smaller hunks), and q (stop). Then check your selection with git diff --staged before committing.

Unstage or discard a change

If you staged the wrong file, remove it from the next commit while keeping your edits:

git restore --staged paper.tex

If instead you want to throw away unstaged edits and restore the last committed version:

git restore -- paper.tex

 Caution

The second command discards work from the working copy. Read git status and git diff first, and name the file explicitly. Avoid destructive catch-all commands copied from the Internet when you are unsure what state the repository is in.

Collaborate without overwriting anyone

Give each coauthor access to the remote repository and have each person clone it into a normal local folder. Do not put one working repository inside a shared Dropbox/OneDrive folder: simultaneous synchronization of the .git directory is a different coordination mechanism and can corrupt or confuse the repository. Each author should have a separate clone and exchange commits through the Git remote.

The simplest collaboration rule is:

Pull before starting, commit coherent changes, and push when they are ready for the others.

Frequent small commits and pushes reduce the chance that two people spend days rewriting the same passage independently. Git still handles that situation, but it cannot decide the mathematics for you.

When a push is rejected

Suppose you commit locally, but a coauthor pushes another commit first. Your git push will be rejected because moving the remote branch to your commit would drop theirs. This is a safety feature.

Fetch and merge the shared work explicitly:

git pull --no-rebase

If the changes are compatible, Git creates the combined history automatically (possibly asking you to confirm a merge message). Then run the usual checks and push:

git status
git push

Another common policy is git pull --rebase, which replays your unpublished local commits on top of the remote history and produces a linear graph. Rebasing is useful, but because it rewrites commit IDs I recommend learning it only after the merge-based workflow is comfortable. Do not rebase commits that collaborators may already have based work on.

Resolve a merge conflict

A conflict means that Git found overlapping edits and refused to guess the intended result. It does not mean that either version has been lost.

After git pull --no-rebase, Git may report a conflict in paper.tex and insert markers like these:

<<<<<<< HEAD
We now prove that the map is a quasi-isomorphism.
=======
We claim that the comparison morphism is a weak equivalence.
>>>>>>> origin/main

The section above ======= is the version from your current branch; the section below it is the incoming version. The labels can be commit IDs or branch names rather than exactly HEAD and origin/main.

Resolve the conflict as follows:

  1. Run git status to list every conflicted file.

  2. Open each file and decide on the final text. You may keep either version, combine them, or write something new.

  3. Remove all three marker lines (<<<<<<<, =======, and >>>>>>>).

  4. Compile and read the affected part of the paper.

  5. Mark the file as resolved, review, and finish the merge:

    git add paper.tex
    git diff --staged
    git commit
    git push

The plain git commit opens an editor containing a suitable merge message. Save it as written or explain any non-obvious resolution.

If you are not ready to resolve the conflict, return to the state before the merge:

git merge --abort

For a conflict during a rebase, the corresponding escape hatch is git rebase --abort.

Make LaTeX conflicts less painful

Git’s ordinary merge algorithm is line-oriented. A few writing habits make a substantial difference:

  • Put each sentence on its own source line. LaTeX normally turns source newlines inside a paragraph into spaces, while Git can then merge edits to different sentences independently.
  • Split a long article into logical files such as introduction.tex, section-models.tex, and section-formality.tex using \input or \include.
  • Avoid global reformatting while a coauthor has unmerged changes.
  • Coordinate before renaming labels, bibliography keys, commands, or files throughout the project.
  • Commit generated figures only when necessary; otherwise commit the code and data that produce them.

These practices also make git diff much more useful as a mathematical proofreading tool.

Use a branch for experimental work

The shared main branch is sufficient for many papers. Create a separate branch when a change is speculative, long-running, or not yet suitable for coauthors:

git switch main
git pull --ff-only
git switch -c rewrite-proposition-3

# edit, test, stage, and commit as usual
git add paper.tex
git commit -m "Try a spectral-sequence proof of Proposition 3"

To incorporate the result locally:

git switch main
git pull --ff-only
git merge rewrite-proposition-3
git push
git branch -d rewrite-proposition-3

If you want coauthors to review or contribute to the branch first, publish it instead:

git push -u origin rewrite-proposition-3

You can then open a pull request on GitHub/GitLab or simply tell your coauthors which branch to inspect. The same conflict-resolution principles apply when the branch is eventually merged.

Mark arXiv and journal versions with tags

Immediately after submitting a version, attach an annotated tag to the exact commit:

git status
git tag -a arxiv-v1 -m "Version submitted to arXiv"
git push origin arxiv-v1

The clean git status check matters: a tag points to a commit and therefore cannot include uncommitted last-minute edits. Repeat with names such as arxiv-v2, submitted-v1, or accepted as the paper evolves.

List tags or inspect one with:

git tag --list
git show arxiv-v1

Recover and undo safely

Git is most reassuring when something has gone wrong. Start by inspecting the history rather than immediately running a command that changes it:

git status
git log --oneline --decorate --graph --all
git show COMMIT_ID

To view a particular file as it appeared at a tag without changing anything:

git show arxiv-v1:paper.tex

To bring that version of the file into a clean working tree, then review and commit it as a new change:

git restore --source=arxiv-v1 -- paper.tex
git diff
git add paper.tex
git commit -m "Restore the arXiv v1 proof"

If a complete published commit was a mistake, git revert COMMIT_ID creates a new commit that applies its inverse. This preserves the shared history and is generally safer for collaborative branches than rewriting it with reset or commit --amend.

When in doubt, make a fresh branch before experimenting:

git switch -c recovery-attempt

Branches are cheap, and a named pointer is easier to recover than an instruction copied in a panic.

A compact checklist

For ordinary work on a shared paper:

  1. git pull --ff-only
  2. edit and compile
  3. git status and git diff
  4. git add ...
  5. git diff --staged
  6. git commit -m "Explain the change"
  7. git push

Before a submission, also check that the working tree is clean and create an annotated tag. If a command refuses to proceed, read the message and run git status: Git is usually protecting history, not demanding that you delete anything.

This is enough Git for the full life cycle of many mathematical papers. The Pro Git Book, the Git reference documentation, and Git’s command-line cheat sheet cover the many useful features left out here.