|
34 | 34 | //! |
35 | 35 | //! All this makes the Graph the **new core data-structure** that is the world of GitButler and upon which visualisations and |
36 | 36 | //! mutation operations are based. |
| 37 | +//! |
| 38 | +//! ### New Workspace Concepts |
| 39 | +//! |
| 40 | +//! The workspace is merely a projection of *The Graph*, and as such is mostly useful for display and user interaction. |
| 41 | +//! In the end it boils down to passing commit-hashes, or [segment-ids](SegmentIndex) at most. |
| 42 | +//! |
| 43 | +//! The workspace has been redesigned from the ground up for flexibility, enabling new user-experiences. To help thinking |
| 44 | +//! about these, a few new concepts will be good to know about. |
| 45 | +//! |
| 46 | +//! #### Entrypoint |
| 47 | +//! |
| 48 | +//! *The Graph* knows where its traversal started as *Entrypoint*, even though it may extend beyond the entrypoint as it |
| 49 | +//! needs to discover possible surrounding workspaces and the target branches that come with them. |
| 50 | +//! In practice, the entrypoint relates to the position of the Git `HEAD` reference, and with that it relates to what |
| 51 | +//! the user currently sees in their worktree. |
| 52 | +//! |
| 53 | +//! #### Early End of Traversal |
| 54 | +//! |
| 55 | +//! During traversal there are mandatory goals, but when reached the traversal usually obeys a limit, if configured. |
| 56 | +//! This is particularly relevant in open-ended traversals outside of workspaces, they can go on until the end of history, |
| 57 | +//! literally. |
| 58 | +//! |
| 59 | +//! For that reason, whenever a commit isn't the end of the graph, but the end traversal as a [limit was hit](init::Options::with_limit_hint), |
| 60 | +//! it will be flagged as such. |
| 61 | +//! |
| 62 | +//! This way one can visualize such Early Ends, and allow the user to extend the traversal selectively the next time it |
| 63 | +//! is performed. |
| 64 | +//! |
| 65 | +//! Despite that, one has to learn how to deal with possible huge graphs, and possible workspaces with a lot of commits, |
| 66 | +//! and [a hard limit](init::Options::with_hard_limit()) as long as downstream cannot deal with this on their own. |
| 67 | +//! |
| 68 | +//! #### Managed Workspaces, and unmanaged ones |
| 69 | +//! |
| 70 | +//! A Workspace is considered managed if it [has workspace metadata](projection::Workspace::metadata). This is typically |
| 71 | +//! only the case for workspaces that have been created by GitButler. |
| 72 | +//! |
| 73 | +//! Workspaces without such metadata can be anything, and are usually just made up to allow GitButler to work with it based |
| 74 | +//! on any `HEAD` position. These should be treated with care, and multi-lane workflows should generally be avoided - these |
| 75 | +//! are reserved to managed Workspaces with the managed merge commit that comes with them. |
| 76 | +//! |
| 77 | +//! #### Optional Targets |
| 78 | +//! |
| 79 | +//! Even on *Managed Workspaces*, target references are now optional. This makes it possible to have a workspace that doesn't |
| 80 | +//! know if it's integrated or not. These are the reason a [soft limit](init::Options::with_limit_hint()) must always be set |
| 81 | +//! to assure the traversal doesn't fetch the entire Git history. |
| 82 | +//! |
| 83 | +//! This, however, also means that the workspace creation doesn't have to be interrupted by a "what's your target" prompt anymore. |
| 84 | +//! Instead, this can be prompted once an action first requires it. |
| 85 | +//! |
| 86 | +//! #### Commit Flags and Segment Flags |
| 87 | +//! |
| 88 | +//! For convenience, various boolean parameters have been aggregated into [bitflags](Commit::flags). Thanks to the way *The Graph* |
| 89 | +//! is traversed, we know that the first commit of any [graph segment](Segment) will always bear the flags that are also used by every other commit |
| 90 | +//! contained within it. Thus, [segment flags](Segment::non_empty_flags_of_first_commit()) are equivalent to the flags of |
| 91 | +//! their first commit. |
| 92 | +//! |
| 93 | +//! The same is *not* true for [stack segments](projection::StackSegment), i.e. segments within a [workspace projection](projection::Workspace). |
| 94 | +//! The reason for this is that they are first-parent aggregations of one *or more* [graph segments](Segment), and thus have multiple |
| 95 | +//! sets of flags, possibly one per [segment](Segment). |
| 96 | +//! |
| 97 | +//! #### The 'frozen' Commit-Flag |
| 98 | +//! |
| 99 | +//! Commits now have a new state that tells for each if it is reachable by *any* remote, and further, if it's reachable |
| 100 | +//! by the remote configured for *their segment*. |
| 101 | +//! |
| 102 | +//! This additional partitioning could be leveraged for enhanced user experiences. |
| 103 | +//! |
| 104 | +//! ### The Graph - Traversal and more |
| 105 | +//! |
| 106 | +//! There are three distinct steps to processing the git commit-graph into more usable forms. |
| 107 | +//! |
| 108 | +//! * **traversal** |
| 109 | +//! - walk the git commit graph to produce a segmented graph, which assigns commits to segments, |
| 110 | +//! but also splits segments on incoming and multiple outgoing connections. |
| 111 | +//! * **reconciliation** |
| 112 | +//! - a post-processing step which adds workspace metadata into the segmented graph, as such information |
| 113 | +//! can't be held in the commit-graph itself. |
| 114 | +//! * **projection** |
| 115 | +//! - transform the segmented and reconciled graph into a view that is application-specific, i.e. see |
| 116 | +//! stacks of first-parent traversed named segments. |
| 117 | +//! |
| 118 | +//! #### Commits are owned by Segments |
| 119 | +//! |
| 120 | +//! A commit can only be owned by a single segment. Thus, there are empty *named* segments which point at other segments, |
| 121 | +//! effectively representing a reference. |
| 122 | +//! Which of these references gets to own a commit depends on the traversal logic, or can be the result of *Reconciliation*. |
| 123 | +//! |
| 124 | +//! #### Reconciliation |
| 125 | +//! |
| 126 | +//! *The Graph* is created from traversing the Git commit graph. Thus, information that is not contained in it has to be |
| 127 | +//! reconciled with *what was actually traversed*. |
| 128 | +//! |
| 129 | +//! Nonetheless, we can create *stacks* as independent branches and dependent branches inside of them without having |
| 130 | +//! a single commit to differentiate their respective branches from each other. |
| 131 | +//! |
| 132 | +//! Imagine a repository with a single commit `73a30f8` with the following Git references pointing to it: `gitbutler/workspace`, |
| 133 | +//! `stack1-segment1`, `stack1-segment2`, `stack2-segment1`, and `refs/remotes/origin/main`. |
| 134 | +//! |
| 135 | +//! Right after traversal, a Graph would look like this: |
| 136 | +//! |
| 137 | +//! ```text |
| 138 | +//! ┌────────────────────┐ |
| 139 | +//! │ origin/main │ |
| 140 | +//! └────────────────────┘ |
| 141 | +//! │ |
| 142 | +//! ▼ |
| 143 | +//! ┌────────────────────────┐ |
| 144 | +//! │gitbutler/workspace │ |
| 145 | +//! │------------------------│ |
| 146 | +//! │73a30f8 ►stack1-segment1│ |
| 147 | +//! │ ►stack1-segment2│ |
| 148 | +//! │ ►stack2-segment1│ |
| 149 | +//! │ ►main │ |
| 150 | +//! └────────────────────────┘ |
| 151 | +//! ``` |
| 152 | +//! |
| 153 | +//! This is due to `gitbutler/workspace` finding `73a30f8` first, with `origin/main` arriving later, pointing to the |
| 154 | +//! first commit in `gitbutler/workspace` effectively. The other references aren't participating in the traversal. |
| 155 | +//! |
| 156 | +//! The tip that finds the commit first is dependent on various factors, and it could also happen that `origin/main` finds |
| 157 | +//! it first. In any case, this needs to be adjusted after traversal in the process called *reconiliation*, so the graph |
| 158 | +//! matches what our [workspace metadata](but_core::ref_metadata::Workspace::stacks) says it should be. |
| 159 | +//! |
| 160 | +//! After reconciling, the graph would become this: |
| 161 | +//! |
| 162 | +//! ```text |
| 163 | +//! ┌────────────────────┐ |
| 164 | +//! │ origin/main │ |
| 165 | +//! └────────────────────┘ |
| 166 | +//! │ ┌────────────────────┐ |
| 167 | +//! │ │gitbutler/workspace │ |
| 168 | +//! │ └────────────────────┘ |
| 169 | +//! │ │ |
| 170 | +//! │ ┌─────────┴─────────┐ |
| 171 | +//! │ │ │ |
| 172 | +//! │ ▼ │ |
| 173 | +//! │ ┌───────────────┐ │ |
| 174 | +//! │ │stack1-segment1│ ▼ |
| 175 | +//! │ └───────────────┘ ┌───────────────┐ |
| 176 | +//! │ │ │stack2-segment1│ |
| 177 | +//! │ ▼ └───────────────┘ |
| 178 | +//! │ ┌───────────────┐ │ |
| 179 | +//! │ │stack1-segment2│ │ |
| 180 | +//! │ └───────────────┘ │ |
| 181 | +//! │ │ │ |
| 182 | +//! │ └─────────┬─────────┘ |
| 183 | +//! │ │ |
| 184 | +//! │ ▼ |
| 185 | +//! │ ┌─────────┐ |
| 186 | +//! │ │ main │ |
| 187 | +//! └─────────────────▶│ ------- │ |
| 188 | +//! │ 73a30f8 │ |
| 189 | +//! └─────────┘ |
| 190 | +//! ``` |
| 191 | +//! |
| 192 | +//! #### Projection |
| 193 | +//! |
| 194 | +//! A projection is a mapping of the segmented graph to any shape an application needs, and for any purpose. |
| 195 | +//! It cannot be stressed enough that the source of truth for all commit-graph manipulation must be the segmented graph, |
| 196 | +//! as projections are inherently lossy. |
| 197 | +//! Thus, it's useful create projects with links back to the segments that the information was extracted from. |
37 | 198 | #![forbid(unsafe_code)] |
38 | 199 | #![deny(missing_docs, rust_2018_idioms)] |
39 | 200 |
|
|
0 commit comments