Next: , Previous: , Up: Miscellaneous   [Contents][Index]

8.7 Wip Modes

Git keeps committed changes around long enough for users to recover changes they have accidentally deleted. It does so by not garbage collecting any committed but no longer referenced objects for a certain period of time, by default 30 days.

But Git does not keep track of uncommitted changes in the working tree and not even the index (the staging area). Because Magit makes it so convenient to modify uncommitted changes, it also makes it easy to shoot yourself in the foot in the process.

For that reason Magit provides three global modes that save tracked files to work-in-progress references after or before certain actions. (Untracked files are never saved and these modes also only work after the first commit has been created).

Two separate work-in-progress references are used to track the state of the index and of the working tree: "refs/wip/index/<branchref>" and "refs/wip/wtree/<branchref>", where <branchref> is the full ref of the current branch, e.g. "refs/heads/master". When the HEAD is detached then "HEAD" is in place of <branchref>.

Checking out another branch (or detaching HEAD) causes the use of different wip refs for subsequent changes, but the old refs are not deleted.

Creating a commit and then making a change causes the wip refs to be recreated to fork from the new commit. But the old commits on the wip refs are not lost. They are still available from the reflog. To make it easier to see when the fork point of a wip ref was changed, an additional commit with the message "restart autosaving" is created on it (xxO commits below are such boundary commits).

Starting with

      BI0---BI1    refs/wip/index/refs/heads/master
A---B              refs/heads/master
      BW0---BW1    refs/wip/wtree/refs/heads/master

and committing the staged changes and editing and saving a file would result in

      BI0---BI1        refs/wip/index/refs/heads/master
A---B---C              refs/heads/master
     \   \
      \   CW0---CW1    refs/wip/wtree/refs/heads/master
        BW0---BW1      refs/wip/wtree/refs/heads/master@{2}

The fork-point of the index wip ref is not changed until some change is being staged. Likewise just checking out a branch or creating a commit does not change the fork-point of the working tree wip ref. The fork-points are not adjusted until there actually is a change that should be committed to the respective wip ref.

User Option: magit-wip-merge-branch

This option controls whether the current branch is merged into the wip refs after a new commit was created on the branch. If nil (currently the default), then no merge is perfomed and wip refs are reset as described above.

If this is non-nil and the current branch has new commits, then it is merged into the wip ref before creating a new wip commit. This makes it easier to inspect wip history and the wip commits are never garbage collected.

  *--*--*--*--*--*       refs/wip/index/refs/heads/master
 /     /     /
A-----B-----C            refs/heads/master

To view the log for a branch and its wip refs use the commands magit-wip-log and magit-wip-log-current. You should use --graph when using these commands. Alternatively you can use the reflog to show all commits that ever existed on a wip ref. You can then recover lost changes from the commits shown in the log or reflog.

Command: magit-wip-log

This command shows the log for a branch and its wip refs.

With a negative prefix argument only the worktree wip ref is shown. The absolute numeric value of the prefix argument controls how many "branches" of each wip ref are shown.

Command: magit-wip-log-current

This command shows the log for the current branch and its wip refs.

With a negative prefix argument only the worktree wip ref is shown. The absolute numeric value of the prefix argument controls how many "branches" of each wip ref are shown.

X w     (magit-reset-worktree)

This command resets the working tree to some commit read from the user and defaulting to the commit at point, while keeping the HEAD and index as-is.

This can be used to restore files to the state committed to a wip ref. Note that this will discard any unstaged changes that might have existed before invoking this command (but of course only after committing that to the working tree wip ref).

There exists a total of three global modes that save to the wip refs, which might seem excessive, but allows fine tuning of when exactly changes are being committed to the wip refs. Enabling all modes makes it less likely that a change slips through the cracks.

Setting the following variables directly does not take effect; either customize them or call the respective mode function.

User Option: magit-wip-after-save-mode

When this mode is enabled, then saving a buffer that visits a file tracked in a Git repository causes its current state to be committed to the working tree wip ref for the current branch.

User Option: magit-wip-after-apply-mode

When this mode is enabled, then applying (i.e. staging, unstaging, discarding, reversing, and regularly applying) a change to a file tracked in a Git repository causes its current state to be committed to the index and/or working tree wip refs for the current branch.

If you only ever edit files using Emacs and only ever interact with Git using Magit, then the above two modes should be enough to protect each and every change from accidental loss. In practice nobody does that. So an additional mode exists that does commit to the wip refs before making changes that could cause the loss of earlier changes.

User Option: magit-wip-before-change-mode

When this mode is enabled, then certain commands commit the existing changes to the files they are about to make changes to.

Function: magit-wip-commit-initial-backup

Adding this function to before-save-hook causes the current version of the file to be committed to the worktree wip ref before the modifications in the buffer are saved. It backs up the same version of the file as backup-buffer would but, instead of using a backup file as backup-buffer would, it uses the same worktree wip ref as used by the various Magit Wip modes. Like backup-buffer, it only does this once; unless you kill the buffer and visit the file again only one backup will be created per Emacs session.

This function ignores the variables that affect backup-buffer and can be used along-side that function, which is recommended because this function only backs up files that are tracked in a Git repository.

Note that even if you enable all three modes and add the above function to the intended hook, this won’t give you perfect protection. The most likely scenario for losing changes despite the use of these modes is making a change outside Emacs and then destroying it also outside Emacs. In some such a scenario, Magit, being an Emacs package, didn’t get the opportunity to keep you from shooting yourself in the foot.

When you are unsure whether Magit did commit a change to the wip refs, then you can explicitly request that all changes to all tracked files are being committed.

M-x magit-wip-commit     (magit-wip-commit)

This command commits all changes to all tracked files to the index and working tree work-in-progress refs. Like the modes described above, it does not commit untracked files, but it does check all tracked files for changes. Use this command when you suspect that the modes might have overlooked a change made outside Emacs/Magit.

User Option: magit-wip-after-save-local-mode-lighter

Mode-line lighter for magit-wip-after-save-local-mode.

User Option: magit-wip-after-apply-mode-lighter

Mode-line lighter for magit-wip-after-apply-mode.

User Option: magit-wip-before-change-mode-lighter

Mode-line lighter for magit-wip-before-change-mode.

User Option: magit-wip-namespace

The namespace used for work-in-progress refs. It has to end with a slash. The wip refs are named "<namespace>index/<branchref>" and "<namespace>wtree/<branchref>". When snapshots are created while the HEAD is detached then "HEAD" is used in place of <branchref>.

Next: , Previous: , Up: Miscellaneous   [Contents][Index]