https://xkcd.com/1597/

Create a branch from unstaged/uncommitted changes on main

Context: I am working on main and adding a simple feature. After a few minutes I realise it is not so simple, and that it would have been better to work on a separate branch.

This happens to me all the time: I have uncommitted changes on main, but I want to move them to a new branch and leave main clean.

See this question on Stackoverflow. If the changes are staged, unstage them first:

git restore --staged filename

or, for all staged files:

git restore --staged .

Then create and switch to a new branch:

git switch -c new-feature

Your working tree and index are preserved, so all your uncommitted changes are now on new-feature.

The usual sequence is:

  • git switch -c new-feature (create a new development branch)
  • (do what you have to do)
  • git commit
  • git switch main
  • git merge --squash new-feature (merge the new feature)
  • git push origin main
  • git branch -D new-feature (delete the local development branch. When using git merge --squash, Git does not record a merge relationship between main and new-feature. Even if all changes from new-feature are now present on main, git branch -d new-feature may refuse to delete the branch because it is not considered merged by ancestry)
  • git push origin --delete new-feature (possibly delete the development branch from the central repository)

How to find the changeset that introduced a bug

It often happens that something is broken and has been broken for a while. With git bisect, you can find the commit that introduced the bug.

Start by telling Git that the current revision is bad:

git bisect start
git bisect bad

Then mark an older revision, where the bug was not present, as good:

git bisect good HEAD~100

Git now checks out a revision roughly halfway between the good and bad commits.

Test the software:

ctest

Then tell Git whether this revision is good or bad:

git bisect good

or:

git bisect bad

Repeat the test-and-mark cycle until Git identifies the first bad commit.

When you are done, return the repository to its normal state:

git bisect reset

View the change history of a file

Use

gitk filename

or, to follow filename past renames:

gitk --follow filename

Since gitk isn't part of the basic git package, you can also consider:

git log --follow -p filename

The -p option lets Git generate the patches for each log entry.

Ignore local files without changing .gitignore

For personal files that should stay untracked, but should not become part of the project-wide .gitignore, use .git/info/exclude.

For example:

echo ".dir-locals-2.el" >> .git/info/exclude

This is useful for editor-local files, local notes, or temporary helper files that are specific to your checkout.

https://xkcd.com/378/

Settings

An optional .dir-locals.el template is provided for Emacs users. It enables the ULTRA Emacs style and sets project-local C++ settings for c++-mode and c++-ts-mode, including Flycheck/Clang, C++23, and the project src/ include path.

To enable it, copy the template to the repository root:

cp misc/dot-dir-locals.el .dir-locals.el

Since the template uses directory-local eval forms, Emacs may ask for confirmation before applying it the first time.

Using ediff as git mergetool

Add the following sections to the .gitconfig file (code from here):

[merge]
    tool = ediff
[mergetool.ediff]
    cmd = emacs --eval \"\
(progn\
  (defun ediff-write-merge-buffer ()\
    (let ((file ediff-merge-store-file))\
      (set-buffer ediff-buffer-C)\
      (write-region (point-min) (point-max) file)\
      (message \\\"Merge buffer saved in: %s\\\" file)\
      (set-buffer-modified-p nil)\
      (sit-for 1)))\
  (setq ediff-quit-hook 'kill-emacs\
        ediff-quit-merge-hook 'ediff-write-merge-buffer)\
  (ediff-merge-files-with-ancestor \\\"$LOCAL\\\" \\\"$REMOTE\\\"\
                                   \\\"$BASE\\\" nil \\\"$MERGED\\\"))\"

Now when you run git mergetool, Emacs will be used (just edit the conflicting merge, save and exit to proceed to the next one).

Convert DOS to Unix line terminator via Emacs

To convert the line ending for existing files a useful command to remember is:

M-x set-buffer-file-coding-system RET iso-8859-15-unix
(undecided-unix is another valid option)

and now save the file with its new buffer coding system:

C-x C-s

Code completion via Irony-Server

AI assisted development

We use my_codex package for integrating OpenAI Codex CLI / Google Antigravity into Emacs.

Local Emacs project settings

For personal Emacs settings that should not be committed, use .dir-locals-2.el. For example, to make my-codex-project-build use the ./setup_build:

((nil . ((compile-command . "./setup_build"))))

Other areas

Debug vs Release in CMake

It's generally best to do an out of source build. From the ultra/src directory:

Unix

$ cmake -DCMAKE_BUILD_TYPE=Release -B build/ src/
$ cmake --build build/

and for Debug:

$ cmake -DCMAKE_BUILD_TYPE=Debug -B build/ src/
$ cmake --build build/

Visual Studio

For multi-configuration generators CMAKE_BUILD_TYPE doesn't work, build type must be specified at build time:

$ cmake -B build/ src/
$ cmake --build build/ --config Release

and for Debug:

$ cmake -B build/ src/
$ cmake --build build/ --config Debug