Blame docs/checkout-internals.md

Packit Service 20376f
Checkout Internals
Packit Service 20376f
==================
Packit Service 20376f
Packit Service 20376f
Checkout has to handle a lot of different cases.  It examines the
Packit Service 20376f
differences between the target tree, the baseline tree and the working
Packit Service 20376f
directory, plus the contents of the index, and groups files into five
Packit Service 20376f
categories:
Packit Service 20376f
Packit Service 20376f
1. UNMODIFIED - Files that match in all places.
Packit Service 20376f
2. SAFE - Files where the working directory and the baseline content
Packit Service 20376f
   match that can be safely updated to the target.
Packit Service 20376f
3. DIRTY/MISSING - Files where the working directory differs from the
Packit Service 20376f
   baseline but there is no conflicting change with the target.  One
Packit Service 20376f
   example is a file that doesn't exist in the working directory - no
Packit Service 20376f
   data would be lost as a result of writing this file.  Which action
Packit Service 20376f
   will be taken with these files depends on the options you use.
Packit Service 20376f
4. CONFLICTS - Files where changes in the working directory conflict
Packit Service 20376f
   with changes to be applied by the target.  If conflicts are found,
Packit Service 20376f
   they prevent any other modifications from being made (although there
Packit Service 20376f
   are options to override that and force the update, of course).
Packit Service 20376f
5. UNTRACKED/IGNORED - Files in the working directory that are untracked
Packit Service 20376f
   or ignored (i.e. only in the working directory, not the other places).
Packit Service 20376f
Packit Service 20376f
Right now, this classification is done via 3 iterators (for the three
Packit Service 20376f
trees), with a final lookup in the index.  At some point, this may move to
Packit Service 20376f
a 4 iterator version to incorporate the index better.
Packit Service 20376f
Packit Service 20376f
The actual checkout is done in five phases (at least right now).
Packit Service 20376f
Packit Service 20376f
1. The diff between the baseline and the target tree is used as a base
Packit Service 20376f
   list of possible updates to be applied.
Packit Service 20376f
2. Iterate through the diff and the working directory, building a list of
Packit Service 20376f
   actions to be taken (and sending notifications about conflicts and
Packit Service 20376f
   dirty files).
Packit Service 20376f
3. Remove any files / directories as needed (because alphabetical
Packit Service 20376f
   iteration means that an untracked directory will end up sorted *after*
Packit Service 20376f
   a blob that should be checked out with the same name).
Packit Service 20376f
4. Update all blobs.
Packit Service 20376f
5. Update all submodules (after 4 in case a new .gitmodules blob was
Packit Service 20376f
   checked out)
Packit Service 20376f
Packit Service 20376f
Checkout could be driven either off a target-to-workdir diff or a
Packit Service 20376f
baseline-to-target diff.  There are pros and cons of each.
Packit Service 20376f
Packit Service 20376f
Target-to-workdir means the diff includes every file that could be
Packit Service 20376f
modified, which simplifies bookkeeping, but the code to constantly refer
Packit Service 20376f
back to the baseline gets complicated.
Packit Service 20376f
Packit Service 20376f
Baseline-to-target has simpler code because the diff defines the action to
Packit Service 20376f
take, but needs special handling for untracked and ignored files, if they
Packit Service 20376f
need to be removed.
Packit Service 20376f
Packit Service 20376f
The current checkout implementation is based on a baseline-to-target diff.
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
Picking Actions
Packit Service 20376f
===============
Packit Service 20376f
Packit Service 20376f
The most interesting aspect of this is phase 2, picking the actions that
Packit Service 20376f
should be taken.  There are a lot of corner cases, so it may be easier to
Packit Service 20376f
start by looking at the rules for a simple 2-iterator diff:
Packit Service 20376f
Packit Service 20376f
Key
Packit Service 20376f
---
Packit Service 20376f
- B1,B2,B3 - blobs with different SHAs,
Packit Service 20376f
- Bi       - ignored blob (WD only)
Packit Service 20376f
- T1,T2,T3 - trees with different SHAs,
Packit Service 20376f
- Ti       - ignored tree (WD only)
Packit Service 20376f
- S1,S2    - submodules with different SHAs
Packit Service 20376f
- Sd       - dirty submodule (WD only)
Packit Service 20376f
- x        - nothing
Packit Service 20376f
Packit Service 20376f
Diff with 2 non-workdir iterators
Packit Service 20376f
---------------------------------
Packit Service 20376f
Packit Service 20376f
|    | Old | New |                                                            |
Packit Service 20376f
|----|-----|-----|------------------------------------------------------------|
Packit Service 20376f
|  0 |   x |   x | nothing                                                    |
Packit Service 20376f
|  1 |   x |  B1 | added blob                                                 |
Packit Service 20376f
|  2 |   x |  T1 | added tree                                                 |
Packit Service 20376f
|  3 |  B1 |   x | removed blob                                               |
Packit Service 20376f
|  4 |  B1 |  B1 | unmodified blob                                            |
Packit Service 20376f
|  5 |  B1 |  B2 | modified blob                                              |
Packit Service 20376f
|  6 |  B1 |  T1 | typechange blob -> tree                                    |
Packit Service 20376f
|  7 |  T1 |   x | removed tree                                               |
Packit Service 20376f
|  8 |  T1 |  B1 | typechange tree -> blob                                    |
Packit Service 20376f
|  9 |  T1 |  T1 | unmodified tree                                            |
Packit Service 20376f
| 10 |  T1 |  T2 | modified tree (implies modified/added/removed blob inside) |
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
Now, let's make the "New" iterator into a working directory iterator, so
Packit Service 20376f
we replace "added" items with either untracked or ignored, like this:
Packit Service 20376f
Packit Service 20376f
Diff with non-work & workdir iterators
Packit Service 20376f
--------------------------------------
Packit Service 20376f
Packit Service 20376f
|    | Old | New |                                                            |
Packit Service 20376f
|----|-----|-----|------------------------------------------------------------|
Packit Service 20376f
|  0 |  x  | x   | nothing                                                    |
Packit Service 20376f
|  1 |  x  | B1  | untracked blob                                             |
Packit Service 20376f
|  2 |  x  | Bi  | ignored file                                               |
Packit Service 20376f
|  3 |  x  | T1  | untracked tree                                             |
Packit Service 20376f
|  4 |  x  | Ti  | ignored tree                                               |
Packit Service 20376f
|  5 | B1  | x   | removed blob                                               |
Packit Service 20376f
|  6 | B1  | B1  | unmodified blob                                            |
Packit Service 20376f
|  7 | B1  | B2  | modified blob                                              |
Packit Service 20376f
|  8 | B1  | T1  | typechange blob -> tree                                    |
Packit Service 20376f
|  9 | B1  | Ti  | removed blob AND ignored tree as separate items            |
Packit Service 20376f
| 10 | T1  | x   | removed tree                                               |
Packit Service 20376f
| 11 | T1  | B1  | typechange tree -> blob                                    |
Packit Service 20376f
| 12 | T1  | Bi  | removed tree AND ignored blob as separate items            |
Packit Service 20376f
| 13 | T1  | T1  | unmodified tree                                            |
Packit Service 20376f
| 14 | T1  | T2  | modified tree (implies modified/added/removed blob inside) |
Packit Service 20376f
Packit Service 20376f
Note: if there is a corresponding entry in the old tree, then a working
Packit Service 20376f
directory item won't be ignored (i.e. no Bi or Ti for tracked items).
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
Now, expand this to three iterators: a baseline tree, a target tree, and
Packit Service 20376f
an actual working directory tree:
Packit Service 20376f
Packit Service 20376f
Checkout From 3 Iterators (2 not workdir, 1 workdir)
Packit Service 20376f
----------------------------------------------------
Packit Service 20376f
Packit Service 20376f
(base == old HEAD; target == what to checkout; actual == working dir)
Packit Service 20376f
Packit Service 20376f
|     |base | target | actual/workdir |                                                                    |
Packit Service 20376f
|-----|-----|------- |----------------|--------------------------------------------------------------------|
Packit Service 20376f
|  0  |   x |      x |      x         | nothing                                                            |
Packit Service 20376f
|  1  |   x |      x | B1/Bi/T1/Ti    | untracked/ignored blob/tree (SAFE)                                 |
Packit Service 20376f
|  2+ |   x |     B1 |      x         | add blob (SAFE)                                                    |
Packit Service 20376f
|  3  |   x |     B1 |     B1         | independently added blob (FORCEABLE-2)                             |
Packit Service 20376f
|  4* |   x |     B1 | B2/Bi/T1/Ti    | add blob with content conflict (FORCEABLE-2)                       |
Packit Service 20376f
|  5+ |   x |     T1 |      x         | add tree (SAFE)                                                    |
Packit Service 20376f
|  6* |   x |     T1 |  B1/Bi         | add tree with blob conflict (FORCEABLE-2)                          |
Packit Service 20376f
|  7  |   x |     T1 |   T1/i         | independently added tree (SAFE+MISSING)                            |
Packit Service 20376f
|  8  |  B1 |      x |      x         | independently deleted blob (SAFE+MISSING)                          |
Packit Service 20376f
|  9- |  B1 |      x |     B1         | delete blob (SAFE)                                                 |
Packit Service 20376f
| 10- |  B1 |      x |     B2         | delete of modified blob (FORCEABLE-1)                              |
Packit Service 20376f
| 11  |  B1 |      x |  T1/Ti         | independently deleted blob AND untrack/ign tree (SAFE+MISSING !!!) |
Packit Service 20376f
| 12  |  B1 |     B1 |      x         | locally deleted blob (DIRTY || SAFE+CREATE)                        |
Packit Service 20376f
| 13+ |  B1 |     B2 |      x         | update to deleted blob (SAFE+MISSING)                              |
Packit Service 20376f
| 14  |  B1 |     B1 |     B1         | unmodified file (SAFE)                                             |
Packit Service 20376f
| 15  |  B1 |     B1 |     B2         | locally modified file (DIRTY)                                      |
Packit Service 20376f
| 16+ |  B1 |     B2 |     B1         | update unmodified blob (SAFE)                                      |
Packit Service 20376f
| 17  |  B1 |     B2 |     B2         | independently updated blob (FORCEABLE-1)                           |
Packit Service 20376f
| 18+ |  B1 |     B2 |     B3         | update to modified blob (FORCEABLE-1)                              |
Packit Service 20376f
| 19  |  B1 |     B1 |  T1/Ti         | locally deleted blob AND untrack/ign tree (DIRTY)                  |
Packit Service 20376f
| 20* |  B1 |     B2 |  T1/Ti         | update to deleted blob AND untrack/ign tree (F-1)                  |
Packit Service 20376f
| 21+ |  B1 |     T1 |      x         | add tree with locally deleted blob (SAFE+MISSING)                  |
Packit Service 20376f
| 22* |  B1 |     T1 |     B1         | add tree AND deleted blob (SAFE)                                   |
Packit Service 20376f
| 23* |  B1 |     T1 |     B2         | add tree with delete of modified blob (F-1)                        |
Packit Service 20376f
| 24  |  B1 |     T1 |     T1         | add tree with deleted blob (F-1)                                   |
Packit Service 20376f
| 25  |  T1 |      x |      x         | independently deleted tree (SAFE+MISSING)                          |
Packit Service 20376f
| 26  |  T1 |      x |  B1/Bi         | independently deleted tree AND untrack/ign blob (F-1)              |
Packit Service 20376f
| 27- |  T1 |      x |     T1         | deleted tree (MAYBE SAFE)                                          |
Packit Service 20376f
| 28+ |  T1 |     B1 |      x         | deleted tree AND added blob (SAFE+MISSING)                         |
Packit Service 20376f
| 29  |  T1 |     B1 |     B1         | independently typechanged tree -> blob (F-1)                       |
Packit Service 20376f
| 30+ |  T1 |     B1 |     B2         | typechange tree->blob with conflicting blob (F-1)                  |
Packit Service 20376f
| 31* |  T1 |     B1 |  T1/T2         | typechange tree->blob (MAYBE SAFE)                                 |
Packit Service 20376f
| 32+ |  T1 |     T1 |      x         | restore locally deleted tree (SAFE+MISSING)                        |
Packit Service 20376f
| 33  |  T1 |     T1 |  B1/Bi         | locally typechange tree->untrack/ign blob (DIRTY)                  |
Packit Service 20376f
| 34  |  T1 |     T1 |  T1/T2         | unmodified tree (MAYBE SAFE)                                       |
Packit Service 20376f
| 35+ |  T1 |     T2 |      x         | update locally deleted tree (SAFE+MISSING)                         |
Packit Service 20376f
| 36* |  T1 |     T2 |  B1/Bi         | update to tree with typechanged tree->blob conflict (F-1)          |
Packit Service 20376f
| 37  |  T1 |     T2 | T1/T2/T3       | update to existing tree (MAYBE SAFE)                               |
Packit Service 20376f
| 38+ |   x |     S1 |      x         | add submodule (SAFE)                                               |
Packit Service 20376f
| 39  |   x |     S1 |  S1/Sd         | independently added submodule (SUBMODULE)                          |
Packit Service 20376f
| 40* |   x |     S1 |     B1         | add submodule with blob confilct (FORCEABLE)                       |
Packit Service 20376f
| 41* |   x |     S1 |     T1         | add submodule with tree conflict (FORCEABLE)                       |
Packit Service 20376f
| 42  |  S1 |      x |  S1/Sd         | deleted submodule (SUBMODULE)                                      |
Packit Service 20376f
| 43  |  S1 |      x |      x         | independently deleted submodule (SUBMODULE)                        |
Packit Service 20376f
| 44  |  S1 |      x |     B1         | independently deleted submodule with added blob (SAFE+MISSING)     |
Packit Service 20376f
| 45  |  S1 |      x |     T1         | independently deleted submodule with added tree (SAFE+MISSING)     |
Packit Service 20376f
| 46  |  S1 |     S1 |      x         | locally deleted submodule (SUBMODULE)                              |
Packit Service 20376f
| 47+ |  S1 |     S2 |      x         | update locally deleted submodule (SAFE)                            |
Packit Service 20376f
| 48  |  S1 |     S1 |     S2         | locally updated submodule commit (SUBMODULE)                       |
Packit Service 20376f
| 49  |  S1 |     S2 |     S1         | updated submodule commit (SUBMODULE)                               |
Packit Service 20376f
| 50+ |  S1 |     B1 |      x         | add blob with locally deleted submodule (SAFE+MISSING)             |
Packit Service 20376f
| 51* |  S1 |     B1 |     S1         | typechange submodule->blob (SAFE)                                  |
Packit Service 20376f
| 52* |  S1 |     B1 |     Sd         | typechange dirty submodule->blob (SAFE!?!?)                        |
Packit Service 20376f
| 53+ |  S1 |     T1 |      x         | add tree with locally deleted submodule (SAFE+MISSING)             |
Packit Service 20376f
| 54* |  S1 |     T1 |  S1/Sd         | typechange submodule->tree (MAYBE SAFE)                            |
Packit Service 20376f
| 55+ |  B1 |     S1 |      x         | add submodule with locally deleted blob (SAFE+MISSING)             |
Packit Service 20376f
| 56* |  B1 |     S1 |     B1         | typechange blob->submodule (SAFE)                                  |
Packit Service 20376f
| 57+ |  T1 |     S1 |      x         | add submodule with locally deleted tree (SAFE+MISSING)             |
Packit Service 20376f
| 58* |  T1 |     S1 |     T1         | typechange tree->submodule (SAFE)                                  |
Packit Service 20376f
Packit Service 20376f
Packit Service 20376f
The number is followed by ' ' if no change is needed or '+' if the case
Packit Service 20376f
needs to write to disk or '-' if something must be deleted and '*' if
Packit Service 20376f
there should be a delete followed by an write.
Packit Service 20376f
Packit Service 20376f
There are four tiers of safe cases:
Packit Service 20376f
Packit Service 20376f
* SAFE         == completely safe to update
Packit Service 20376f
* SAFE+MISSING == safe except the workdir is missing the expect content
Packit Service 20376f
* MAYBE SAFE   == safe if workdir tree matches (or is missing) baseline
Packit Service 20376f
                  content, which is unknown at this point
Packit Service 20376f
* FORCEABLE == conflict unless FORCE is given
Packit Service 20376f
* DIRTY     == no conflict but change is not applied unless FORCE
Packit Service 20376f
* SUBMODULE == no conflict and no change is applied unless a deleted
Packit Service 20376f
               submodule dir is empty
Packit Service 20376f
Packit Service 20376f
Some slightly unusual circumstances:
Packit Service 20376f
Packit Service 20376f
* 8 - parent dir is only deleted when file is, so parent will be left if
Packit Service 20376f
    empty even though it would be deleted if the file were present
Packit Service 20376f
* 11 - core git does not consider this a conflict but attempts to delete T1
Packit Service 20376f
    and gives "unable to unlink file" error yet does not skip the rest
Packit Service 20376f
    of the operation
Packit Service 20376f
* 12 - without FORCE file is left deleted (i.e. not restored) so new wd is
Packit Service 20376f
    dirty (and warning message "D file" is printed), with FORCE, file is
Packit Service 20376f
    restored.
Packit Service 20376f
* 24 - This should be considered MAYBE SAFE since effectively it is 7 and 8
Packit Service 20376f
    combined, but core git considers this a conflict unless forced.
Packit Service 20376f
* 26 - This combines two cases (1 & 25) (and also implied 8 for tree content)
Packit Service 20376f
    which are ok on their own, but core git treat this as a conflict.
Packit Service 20376f
    If not forced, this is a conflict.  If forced, this actually doesn't
Packit Service 20376f
    have to write anything and leaves the new blob as an untracked file.
Packit Service 20376f
* 32 - This is the only case where the baseline and target values match
Packit Service 20376f
    and yet we will still write to the working directory.  In all other
Packit Service 20376f
    cases, if baseline == target, we don't touch the workdir (it is
Packit Service 20376f
    either already right or is "dirty").  However, since this case also
Packit Service 20376f
    implies that a ?/B1/x case will exist as well, it can be skipped.
Packit Service 20376f
* 41 - It's not clear how core git distinguishes this case from 39 (mode?).
Packit Service 20376f
* 52 - Core git makes destructive changes without any warning when the
Packit Service 20376f
    submodule is dirty and the type changes to a blob.
Packit Service 20376f
Packit Service 20376f
Cases 3, 17, 24, 26, and 29 are all considered conflicts even though
Packit Service 20376f
none of them will require making any updates to the working directory.