|
| 1 | +# Fixing conflicts outside of GitButler |
| 2 | + |
| 3 | +If you have ended up with conflicted commits and GitButler is completely |
| 4 | +unresponsive, they can be recovered using plain git commands in the following |
| 5 | +manner: |
| 6 | + |
| 7 | +## Consider hopping on a call with one of us. |
| 8 | + |
| 9 | +The resolution steps make use of some advanced git functions, if you are not |
| 10 | +comfortable with any of the steps - we are more than happy to walk you through |
| 11 | +any recovery processes. |
| 12 | + |
| 13 | +Join our Discord and let us know about your situation. One of us will help you |
| 14 | +work through your problem either through text or via a call. |
| 15 | + |
| 16 | +## Backup! |
| 17 | + |
| 18 | +First, make a copy of your entire repo. We don't want to lose any data if we |
| 19 | +make a mistake in recovery. |
| 20 | + |
| 21 | +## Make a new branch |
| 22 | + |
| 23 | +Conflicts often come up as part of a cherry pick so we want to re-perform |
| 24 | +the rebase manually - resolving any conflicted commits as we go. |
| 25 | + |
| 26 | +I want the commits to sit on top of origin/master, so I'll run the following |
| 27 | +commands to make a new and empty branch to re-build the branch on top of: |
| 28 | + |
| 29 | +``` |
| 30 | +git switch -c reconstruction |
| 31 | +git reset --hard origin/master |
| 32 | +``` |
| 33 | + |
| 34 | +## Looking at the commits |
| 35 | + |
| 36 | +We can now get an idea of what operations we need to perform to reconstruct the |
| 37 | +branch. |
| 38 | + |
| 39 | +By running: |
| 40 | + |
| 41 | +``` |
| 42 | +git log --graph --oneline <original-branch-name> |
| 43 | +``` |
| 44 | + |
| 45 | +We can see all the commits that are in our branch. |
| 46 | + |
| 47 | +For my branch, it looks as follows: |
| 48 | + |
| 49 | +``` |
| 50 | +> git log --oneline --graph reimplement-insert-blank-commit |
| 51 | +* b1b1bf07d (reimplement-insert-blank-commit) Improvements to rebase engine for better composability |
| 52 | +* c8f5b92a0 Rename reword_commit to commit_reword |
| 53 | +* e1fc3b9f5 Reimplement insert blank commit |
| 54 | +``` |
| 55 | + |
| 56 | +We want to work from the bottom of this list to the top. |
| 57 | + |
| 58 | +To get a better idea of the state of a given commit, we can run: |
| 59 | + |
| 60 | +``` |
| 61 | +git cat-file -p <commit-id> |
| 62 | +``` |
| 63 | + |
| 64 | +We can identify if the commit is conflicted by the presence of a header that |
| 65 | +looks as follows: |
| 66 | + |
| 67 | +``` |
| 68 | +gitbutler-conflicted <N> |
| 69 | +``` |
| 70 | + |
| 71 | +## Reconstructing the branch |
| 72 | + |
| 73 | +Depending on the state of a commit and it's parent, there are some different |
| 74 | +operations we want to perform to re-perform the rebase. |
| 75 | + |
| 76 | +### If a commit is conflicted |
| 77 | + |
| 78 | +If a commit is conflicted, we want to first look at the tree of the conflicted |
| 79 | +commit. We can do that with the following command: |
| 80 | + |
| 81 | +``` |
| 82 | +git cat-file -p <commit-id>^{tree} |
| 83 | +``` |
| 84 | + |
| 85 | +For the first commit in my list, that looks like: |
| 86 | + |
| 87 | +``` |
| 88 | +> git cat-file -p e1fc3b9f5^{tree} |
| 89 | +040000 tree 24e291fb0867efec629b933c00aaeaff39365efd .auto-resolution |
| 90 | +040000 tree ffde17e2a4d4c045869b300b4ec9027851581e33 .conflict-base-0 |
| 91 | +100644 blob dca5869dd76a1eeadeba9387ec7f94b318085c7e .conflict-files |
| 92 | +040000 tree 3b23a61344b84fa3f7b93b1ca058d24846a31f57 .conflict-side-0 |
| 93 | +040000 tree b5a91de1f2ce0a248472d03c1701a20289e4d657 .conflict-side-1 |
| 94 | +100644 blob 2af04b7f1384300b742f6112005cddc5a87be022 README.txt |
| 95 | +``` |
| 96 | + |
| 97 | +Here we see the conflicted representation of a commit in GitButler. |
| 98 | + |
| 99 | +There are four entries that are relevant here: |
| 100 | + |
| 101 | +- `.auto-resolution` - This contains a resolution attempt that GitButler made |
| 102 | + when cherry-picking the commit. |
| 103 | +- `.conflict-base-0` - This contains the tree of the commit that was |
| 104 | + cherry-picked to produce the conflicted commit. |
| 105 | +- `.conflict-side-0` - This contains the tree of the commit that we tried to |
| 106 | + cherry-pick onto. |
| 107 | +- `.conflict-side-1` - This contains the tree of the origional commit before it |
| 108 | + was cherry-picked. |
| 109 | + |
| 110 | +To re-perform the cherry-pick that GitButler was trying to do. We do that by |
| 111 | +first making a commit that holds the `.conflict-base-0` tree which can be done |
| 112 | +by running: |
| 113 | + |
| 114 | +``` |
| 115 | +git commit-tree <id-of-conflict-base-0> -p HEAD -m "base" |
| 116 | +``` |
| 117 | + |
| 118 | +For me, that looks like: |
| 119 | + |
| 120 | +``` |
| 121 | +> git commit-tree ffde17e2a4d4c045869b300b4ec9027851581e33 -p HEAD -m "base" |
| 122 | +0100ea63fe63a2894567de42371f8d6cf79e4a85 |
| 123 | +``` |
| 124 | + |
| 125 | +This has given us an OID in return. This is the object ID of whatever that base |
| 126 | +tree contains. |
| 127 | + |
| 128 | +We then want to create a commit that contains the `.conflict-side-1` tree, and |
| 129 | +has that new "base" commit as it's parent. We can do that by running: |
| 130 | + |
| 131 | +``` |
| 132 | +git commit-tree <id-of-conflict-side-1> -p <id-of-base-commit> -m "Desired commit message" |
| 133 | +``` |
| 134 | + |
| 135 | +For me this looks like: |
| 136 | + |
| 137 | +``` |
| 138 | +git commit-tree b5a91de1f2ce0a248472d03c1701a20289e4d657 -p 0100ea63fe63a2894567de42371f8d6cf79e4a85 -m "Reimplement insert blank commit" |
| 139 | +35d518d2ea68635631593faff34b11e3b1904014 |
| 140 | +``` |
| 141 | + |
| 142 | +Using that returned commit OID, we can then bring that commit on top of our |
| 143 | +branch with: |
| 144 | + |
| 145 | +``` |
| 146 | +git cherry-pick <returned commit id> |
| 147 | +``` |
| 148 | + |
| 149 | +For me, that looked like: |
| 150 | + |
| 151 | +``` |
| 152 | +git cherry-pick 35d518d2ea68635631593faff34b11e3b1904014 |
| 153 | +``` |
| 154 | + |
| 155 | +Git may prompt you to solve some conflicts here which you can resolve in the |
| 156 | +standard manner. |
| 157 | + |
| 158 | +### If a commit is **not conflicted**, but has a **conflicted parent**. |
| 159 | + |
| 160 | +If the commit is not conflicted, but the commit before it in your log WAS |
| 161 | +conflicted, then we similarly need to create a commit to cherry-pick on our own. |
| 162 | + |
| 163 | +First, you will want to take a look at that parent's commit tree with: |
| 164 | + |
| 165 | +``` |
| 166 | +git cat-file -p <parent-commit-oid> |
| 167 | +``` |
| 168 | + |
| 169 | +We want to make a base commit that uses the `.auto-resolution` tree. We can do |
| 170 | +that with: |
| 171 | + |
| 172 | +``` |
| 173 | +git commit-tree <id-of-auto-resolution-tree> -p HEAD -m "Desired commit message" |
| 174 | +``` |
| 175 | + |
| 176 | +We then want to make a commit that has the tree of the non-conflicted commit, |
| 177 | +with the parent as the base commit we just made. |
| 178 | + |
| 179 | +We can first find the tree of the non-conflicted commit by running: |
| 180 | + |
| 181 | +``` |
| 182 | +git cat-file -p <unconflicted-commit-id> |
| 183 | +``` |
| 184 | + |
| 185 | +and copying the entry after `tree`. |
| 186 | + |
| 187 | +We then want to make our commit to cherry pick with: |
| 188 | + |
| 189 | +``` |
| 190 | +git commit-tree <id-of-unconflicted-commit> -p <id-of-base-commit> -m "desired commit message" |
| 191 | +``` |
| 192 | + |
| 193 | +We can then cherry-pick that commit with `git cherry-pick` onto our branch, |
| 194 | +following the standard conflict flow if applicable. |
| 195 | + |
| 196 | +### If the commit is **not conflicted** and its parent is **not conflicted** |
| 197 | + |
| 198 | +If this is the case, we can run the standard `git cherry-pick` command to bring |
| 199 | +that commit into our reconstruction branch, following the standard conflict flow |
| 200 | +if applicable. |
| 201 | + |
| 202 | +## Pushing your reconstructed branch |
| 203 | + |
| 204 | +Once you have finished bringing all of your commits into your reconstruction |
| 205 | +branch, you can then push it to your remote via `git push`. |
0 commit comments