├── .gitignore ├── COPYING ├── MANIFEST.in ├── README.md ├── TODO.md ├── completions └── git-imerge ├── doc └── presentations │ └── GitMerge-2013 │ ├── .gitignore │ ├── Makefile │ ├── README.txt │ ├── talk.rst │ └── ui │ └── small-white │ ├── blank.gif │ ├── framing.css │ ├── iepngfix.htc │ ├── opera.css │ ├── outline.css │ ├── pretty.css │ ├── print.css │ ├── s5-core.css │ ├── slides.css │ └── slides.js ├── gitimerge.py ├── imerge.css ├── setup.py ├── t ├── .gitignore ├── test-conflicted ├── test-drop ├── test-duplicated ├── test-flip-flop ├── test-lib.sh ├── test-really-conflicted └── test-unconflicted └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info/ 2 | *.pyc 3 | *.pyo 4 | .tox/ 5 | /*.html 6 | build/ 7 | dist/ 8 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include completions/git-imerge 2 | include COPYING 3 | include t/test-conflicted 4 | include t/test-drop 5 | include t/test-duplicated 6 | include t/test-flip-flop 7 | include t/test-lib.sh 8 | include t/test-really-conflicted 9 | include t/test-unconflicted 10 | include tox.ini 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # git-imerge -- incremental merge and rebase for Git 2 | 3 | Perform a merge between two branches incrementally. If conflicts are encountered, figure out exactly which pairs of commits conflict, and present the user with one pairwise conflict at a time for resolution. 4 | 5 | `git-imerge` has two primary design goals: 6 | 7 | * Reduce the pain of resolving merge conflicts to its unavoidable minimum, by finding and presenting the smallest possible conflicts: those between the changes introduced by one commit from each branch. 8 | 9 | * Allow a merge to be saved, tested, interrupted, published, and collaborated on while it is in progress. 10 | 11 | I think that it is easiest to understand the concept of incremental merging visually, and therefore I recommend the video of my [git-imerge presentation from the GitMerge 2013 conference](https://www.youtube.com/watch?v=FMZ2_-Ny_zc) (20 min) as a good place to start. The full slides for that talk are available in this repository under `doc/presentations/GitMerge-2013`. At the same conference, I was interviewed about `git-imerge` by Thomas Ferris Nicolaisen for his [GitMinutes Podcast #12](https://episodes.gitminutes.com/2013/06/gitminutes-12-git-merge-2013-part-4.html). 12 | 13 | To learn how to use the `git-imerge` tool itself, I suggest the blog article [git-imerge: A Practical Introduction](https://softwareswirl.blogspot.com/2013/05/git-imerge-practical-introduction.html) and also typing `git-imerge --help` and `git-imerge SUBCOMMAND --help`. If you want more information, the theory and benefits of incremental merging are described in minute detail in a series of blog articles [[1](#REF1)], as are the benefits of retaining history when doing a rebase [[2](#REF2)]. 14 | 15 | Multiple incremental merges can be in progress at the same time. Each incremental merge has a name, and its progress is recorded in the Git repository as references under `refs/imerge/NAME`. The current state of an incremental merge can be visualized using the `diagram` command. 16 | 17 | An incremental merge can be interrupted and resumed arbitrarily, or even pushed to a server to allow somebody else to work on it. 18 | 19 | `git-imerge` comes with a Bash completion script, `completions/git-imerge`, which is installed automatically when installing `git-imerge`. 20 | 21 | 22 | ## Requirements 23 | 24 | `git-imerge` requires: 25 | 26 | * A Python interpreter; either 27 | 28 | * Python 3.x, version 3.3 or later. 29 | 30 | * Python 2.x, version 2.6 or later. If you are using Python 2.6.x, then you have to install the `argparse` module yourself, as it was only added to the standard library in Python 2.7. 31 | 32 | * A recent version of Git. 33 | 34 | Bash completion requires Git's completion being available. 35 | 36 | 37 | ## Installation 38 | 39 | `git-imerge` is available on [PyPI](https://pypi.org), so you can install it with `pip`: 40 | 41 | $ pip install git-imerge 42 | 43 | 44 | or using `setup.py` if you have downloaded the source package locally: 45 | 46 | $ python setup.py install 47 | 48 | If you prefer, you can use [Homebrew](https://brew.sh) to install `git-imerge`: 49 | 50 | $ brew install git-imerge 51 | 52 | ## Instructions 53 | 54 | To start a merge or rebase operation using `git-imerge`, you use commands that are similar to the corresponding `git` commands: 55 | 56 | | `git-imerge` command | `git` analogue | Effect | 57 | | -------------------- | -------------- | ------ | 58 | | `git-imerge merge BRANCH` | `git merge BRANCH` | Merge ``BRANCH`` into the current branch | 59 | | `git-imerge rebase BRANCH` | `git rebase BRANCH` | Rebase the current branch on top of ``BRANCH`` | 60 | | `git-imerge revert COMMIT` | `git revert COMMIT` | Add a new commit that undoes the effect of `COMMIT` | 61 | | `git-imerge revert COMMIT1..COMMIT2` | `git revert COMMIT1..COMMIT2` | Add new commits that undo the effects of `COMMIT1..COMMIT2` | 62 | | `git-imerge drop COMMIT` | `git rebase --onto COMMIT^ COMMIT` | Entirely delete commit `COMMIT` from the history of the current branch | 63 | | `git-imerge drop COMMIT1..COMMIT2` | `git rebase --onto COMMIT1 COMMIT2` | Entirely delete commits ``COMMIT1..COMMIT2`` from the history of the current branch | 64 | 65 | `git-imerge drop` is also analogous to running `git rebase --interactive`, then deleting the specified commit(s) from the history. Both the `drop` and the `revert` subcommands are included in git-imerge because the equivalent git operations can conflict, so they both can benefit from using the incremental merge approach.) 66 | 67 | A few more options are available if you start the incremental merge using `git imerge start`: 68 | 69 | $ git-imerge start --name=NAME --goal=GOAL [--first-parent] BRANCH 70 | 71 | where 72 | 73 | * `NAME` is the name for this merge (and also the default name of the branch to which the results will be saved). 74 | 75 | * `GOAL` describes how you want to simplify the results (see next section). 76 | 77 | After the incremental merge is started, you will be presented with any conflicts that have to be resolved. The basic procedure is similar to performing an incremental merge using `git`: 78 | 79 | while not done: 80 | 81 | <"git add" the files that you changed> 82 | git-imerge continue 83 | 84 | When you have resolved all of the conflicts, you finish the incremental merge by typing: 85 | 86 | git-imerge finish 87 | 88 | That should be enough to get you going. All of these subcommands have additional options; to learn about them type: 89 | 90 | git-imerge --help 91 | git-imerge SUBCMD --help 92 | 93 | 94 | ### Simplifying results 95 | 96 | When the incremental merge is finished, you can simplify its results in various ways before recording it in your project's permanent history by using either the `finish` or `simplify` command. The "goal" of the incremental merge can be one of the following: 97 | 98 | * `merge` — keep only a simple merge of the second branch into the first branch, discarding all intermediate merges. The end result is similar to what you would get from 99 | 100 | git checkout BRANCH1 101 | git merge BRANCH2 102 | 103 | * `rebase` — keep the versions of the commits from the second branch rebased onto the first branch. The end result is similar to what you would get from 104 | 105 | git checkout BRANCH2 106 | git rebase BRANCH1 107 | 108 | * `rebase-with-history` — like `rebase`, except that it retains the old versions of the rebased commits in the history. It is equivalent to merging the commits from `BRANCH2` into `BRANCH1`, one commit at a time. In other words, it transforms this: 109 | 110 | o---o---o---o BRANCH1 111 | \ 112 | A---B---C---D BRANCH2 113 | 114 | into this: 115 | 116 | o---o---o---o---A'--B'--C'--D' NEW_BRANCH 117 | \ / / / / 118 | --------A---B---C---D 119 | 120 | It is safe to rebase an already-published branch using this 121 | approach. See [[2](#REF2)] for more information. 122 | 123 | * `full` — don't simplify the incremental merge at all: do all of the intermediate merges and retain them all in the permanent history. In other words, it transforms this: 124 | 125 | o---o---1---2---3 BRANCH1 126 | \ 127 | A---B---C---D BRANCH2 128 | 129 | into this: 130 | 131 | o---o---1---2---3 132 | \ \ \ \ 133 | A---A1--A2--A3 134 | \ \ \ \ 135 | B---B1--B2--B3 136 | \ \ \ \ 137 | C---C1--C2--C3 138 | \ \ \ \ 139 | D---D1--D2--D3 NEW_BRANCH 140 | 141 | This approach retains the complete history and ancestry information, which gives the maximum flexibility for conducting future merges. On the other hand, it clutters up the permanent Git history considerably. 142 | 143 | * `border` — this experimental goal retains the rebase of `BRANCH2` onto `BRANCH1` and also the rebase of `BRANCH1` onto `BRANCH2`, plus a merge commit that includes both branches. In other words, it transforms this: 144 | 145 | o---o---1---2---3 BRANCH1 146 | \ 147 | A---B---C---D BRANCH2 148 | 149 | into this: 150 | 151 | o---o---1---2---3 152 | \ \ 153 | A A2 154 | \ \ 155 | B B2 156 | \ \ 157 | C C2 158 | \ \ 159 | D---D1--D2--D3 NEW_BRANCH 160 | 161 | This approach leaves more history than a simple merge or rebase, possibly making future merges easier. 162 | 163 | * `border-with-history` — this experimental goal retains the `rebase-with-history of` `BRANCH2` onto `BRANCH1` and also the rebase (without history) of `BRANCH1` onto `BRANCH2`, plus a merge commit that includes both branches. In other words, it transforms this: 164 | 165 | o---o---1---2---3 BRANCH1 166 | \ 167 | A---B---C---D BRANCH2 168 | 169 | into this: 170 | 171 | o---o---1---2---3 172 | \ \ 173 | A-----------A3 174 | \ \ 175 | B-----------B3 176 | \ \ 177 | C-----------C3 178 | \ \ 179 | D---D1--D2--D3 NEW_BRANCH 180 | 181 | This approach leaves more history and ancestry information than a simple merge or rebase, possibly making future merges easier. 182 | 183 | * `border-with-history2` — this experimental goal retains the `rebase-with-history` of `BRANCH1` onto `BRANCH2` and also the `rebase-with-history` of `BRANCH2` onto `BRANCH1`, plus a merge commit that includes both branches. In other words, it transforms this: 184 | 185 | o---o---1---2---3 BRANCH1 186 | \ 187 | A---B---C---D BRANCH2 188 | 189 | into this: 190 | 191 | o---o---1---2---3 192 | \ \ \ \ 193 | A--- --- ---A3 194 | \ \ \ \ 195 | B--- --- ---B3 196 | \ \ \ \ 197 | C--- --- ---C3 198 | \ \ \ \ 199 | D---D1--D2--D3 NEW_BRANCH 200 | 201 | This approach leaves more history and ancestry information than a simple merge or rebase, possibly making future merges easier. 202 | 203 | 204 | ## Technical notes 205 | 206 | ### Suspending/resuming 207 | 208 | When `git-imerge` needs to ask the user to do a merge manually, it creates a temporary branch `refs/heads/imerge/NAME` to hold the result. If you want to suspend an incremental merge to do something else before continuing, all you need to do is abort any pending merge using `git merge --abort` and switch to your other branch. When you are ready to resume the incremental merge, just type `git imerge continue`. 209 | 210 | If you need to completely abort an in-progress incremental merge, first remove the temporary branches `git-imerge` creates using `git-imerge remove`, then checkout the branch you were in before you started the incremental merge with `git checkout ORIGINAL_BRANCH`. 211 | 212 | 213 | ### Storage 214 | 215 | `git-imerge` records all of the intermediate state about an incremental merge in the Git object database as a bunch of references under `refs/imerge/NAME`, where `NAME` is the name of the imerge: 216 | 217 | * `refs/imerge/NAME/state` points to a blob that describes the current state of the imerge in JSON format; for example, 218 | 219 | * The tips of the two branches that are being merged 220 | 221 | * The current "blocker" merges (merges that the user will have to do by hand), if any 222 | 223 | * The simplification goal 224 | 225 | * The name of the branch to which the result will be written. 226 | 227 | * `refs/imerge/NAME/manual/I-J` and `refs/imerge/NAME/auto/I-J` refer to the manual and automatic merge commits, respectively, that have been done so far as part of the incremental merge. `I` and `J` are integers indicating the location `(I,J)` of the merge in the incremental merge diagram. 228 | 229 | 230 | ### Transferring an in-progress imerge between repositories 231 | 232 | It might sometimes be convenient to transfer an in-progress incremental merge from one Git repository to another. For example, you might want to make a backup of the current state, or continue an imerge at home that you started at work, or ask a colleague to do a particular pairwise merge for you. Since all of the imerge state is stored in the Git object database, this can be done by pushing/fetching the references named in the previous section. For example, 233 | 234 | $ git push --prune origin +refs/imerge/NAME/*:refs/imerge/NAME/* 235 | 236 | or 237 | 238 | $ git fetch --prune origin +refs/imerge/NAME/*:refs/imerge/NAME/* 239 | 240 | Please note that these commands _overwrite_ any state that already existed in the destination repository. There is currently no support for combining the work done by two people in parallel on an incremental merge, so for now you'll just have to take turns. 241 | 242 | 243 | ### Interaction with `git rerere` 244 | 245 | `git rerere` is a nice tool that records how you resolve merge conflicts, and if it sees the same conflict again it tries to automatically reuse the same resolution. 246 | 247 | Since `git-imerge` attempts so many similar test merges, it is easy to imagine `rerere` getting confused. Moreover, `git-imerge` relies on a merge resolving (or not resolving) consistently if it is carried out more than once. Having `rerere` store extra information behind the scenes could therefore confuse `git-imerge`. 248 | 249 | Indeed, in testing it appeared that during incremental merges, the interaction of `git-imerge` with `rerere` was sometimes causing merge conflicts to be resolved incorrectly. Therefore, `git-imerge` explicitly turns rerere off temporarily whenever it runs any `git` commands. 250 | 251 | 252 | ### Log messages for pairwise merge commits 253 | 254 | When `git imerge continue` or `git imerge record` finds a resolved merge in the working tree, it commits that merge then incorporates it into the incremental merge. Usually it just uses Git's autogenerated commit message for such commits. If you want to be prompted to edit such commit messages, you can either specify `--edit` on the command line or change the default in your configuration: 255 | 256 | $ git config --global imerge.editmergemessages true 257 | 258 | 259 | ## Testing 260 | 261 | `git-imerge` uses [`tox`](https://tox.readthedocs.io) to run tests. To run the test suite with the system's default Python: 262 | 263 | $ tox 264 | 265 | To run with a specific Python version, such as 3.7, pass the `-e` argument to `tox`: 266 | 267 | $ tox -e py37 268 | 269 | 270 | ## License 271 | 272 | `git-imerge` is released as open-source software under the GNU General Public License (GPL), version 2 or later. See file `COPYING` for more information. 273 | 274 | 275 | ## References 276 | 277 | [1] 278 | * https://softwareswirl.blogspot.com/2012/12/the-conflict-frontier-of-nightmare-merge.html 279 | * https://softwareswirl.blogspot.com/2012/12/mapping-merge-conflict-frontier.html 280 | * https://softwareswirl.blogspot.com/2012/12/real-world-conflict-diagrams.html 281 | * https://softwareswirl.blogspot.com/2013/05/git-incremental-merge.html 282 | * https://softwareswirl.blogspot.com/2013/05/one-merge-to-rule-them-all.html 283 | * https://softwareswirl.blogspot.com/2013/05/incremental-merge-vs-direct-merge-vs.html 284 | * https://softwareswirl.blogspot.com/2013/05/git-imerge-practical-introduction.html 285 | 286 | [2] 287 | * https://softwareswirl.blogspot.com/2009/04/truce-in-merge-vs-rebase-war.html 288 | * https://softwareswirl.blogspot.com/2009/08/upstream-rebase-just-works-if-history.html 289 | * https://softwareswirl.blogspot.com/2009/08/rebase-with-history-implementation.html 290 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Ideas for things to do 2 | 3 | ## Bugs 4 | 5 | * Find some bugs and fix them :-) 6 | 7 | * Check that the capstone merge in a block outline is merged correctly. 8 | 9 | * Since the ancestry of its two parents is incomplete, Git would probably try to do a recursive merge. See if we can somehow do better without filling in the whole diagram. 10 | 11 | * Compare the merges that would come from continuing each side of the outline, and verify that they are identical. 12 | 13 | 14 | ## Convenience features 15 | 16 | * Add a two-argument form of the `start` and `init` commands to specify both branches in one go. (Or maybe not, if we want to leave the way open to supporting octopus merges!) Maybe `--onto=X` like rebase? 17 | 18 | * Improve the handling of log messages. Incorporate log messages from manual merge conflicts into suggested log messages for the simplified commits. 19 | 20 | * Allow the user to specify which conflict they would like to resolve next, and set it up for them. 21 | 22 | * Allow the user to block certain merges, meaning that they should never be skipped over or merged automatically. 23 | 24 | * Add a `git imerge pause` that puts an imerge on hold and resets the working copy to a reasonable state. 25 | 26 | * Allow the user to specify a test that is run automatically after each automatic merge, (à la `git bisect run`). 27 | 28 | 29 | ## New merge goals and styles 30 | 31 | * Add a `--goal=sparse` option that retains all of the conflicted merges and enough intermediate history to connect them. 32 | 33 | * Add an option that allows the user to resolve conflicts in larger chunks; for example, add a rebase-with-history-type merge where each branch commit is merged directly to its final location on the last master commit. (Perhaps `--conflicts={pairwise|fewest}`.) 34 | 35 | * Permit switching between goal/conflict choices when they are meaningful while prohibiting nonsensical ones. 36 | 37 | 38 | ## Collaboration 39 | 40 | * Add `git imerge push REMOTE` to push the status of an in-progress merge to REMOTE. 41 | 42 | * Add `git imerge pull REMOTE` to pull the current state of an incremental merge from REMOTE. 43 | 44 | * Add some kind of `fetch`-like functionality that stores the result into a remote namespace. 45 | 46 | * The analogue of "non-fast-forward" for incremental merges is different than for other references. A `push`/`pull` should be prohibited if: 47 | 48 | * Any of the branches is updated in a non-fast-forward fashion. 49 | 50 | * Any commit `I1-I2` is updated (at all!) when there is an existing commit `I1'-I2'` with `I1' >= I1` and `I2' >= I2`. 51 | 52 | * As a safety precaution, the DAG of the retrieved merges should probably be checked for self-consistency (see `git imerge fsck` below). 53 | 54 | * Add smarter ways to reconcile two versions of an in-progress merge that are not fast-forwards of each other. Probably this should work with two arbitrary local merges (one of which is probably from a remote). 55 | 56 | 57 | ## Flexibility 58 | 59 | * Allow commits to be skipped over when merging if, for example, they are broken (analogous to `git bisect skip`). This should be allowed when an imerge is being initialized and also after an imerge is in progress. 60 | 61 | * Allow arbitrary manual merge commits to be recorded, and be smarter about how such commits are integrated. 62 | 63 | * Allow recorded merges to be retroactively rejected, adjusting subsequent merges accordingly. 64 | 65 | * In first version, just discard all commits that depended on it. 66 | 67 | * In later versions, salvage parts of the subsequent merges when possible. 68 | 69 | 70 | ### Relax `--first-parent` limitation 71 | 72 | * Allow recursive merges: if one of the `--first-parent` merges is itself a merge, maybe it can be "exploded" into individual commits and these commits merged with the second branch as part of the first merge or as a subsidiary merge. 73 | 74 | * Allow more complicated topologies in the two branches to be merged, and somehow form the appropriate Cartesian product of their commits with the correct ancestry. 75 | 76 | 77 | ## Tools 78 | 79 | * `git imerge info` -- show a detailed, human-readable summary of all intermediate commits related to this imerge. 80 | 81 | * `git imerge parse M-N` -- show SHA-1 of the specified merge if it has been done. Maybe also `git imerge parse NAME/M-N`. 82 | 83 | * `git imerge show M-N` -- show more, human-readable info about the specified imerge. 84 | 85 | * `git imerge list -v` -- add more information to the display; for example, the goals, status, etc. of each imerge. 86 | 87 | * `git imerge status` -- show the current imerge status (issue #22). If the user has been asked to do a merge, show the log messages for the two original commits again. 88 | 89 | 90 | ## Miscellaneous 91 | 92 | * Add a command `git imerge fsck` (for lack of a better name) that checks the consistency of the intermediate commits (especially their DAG). 93 | 94 | * Maybe expand diagram to 2x2 characters per merge, to make room for more information (e.g., block boundaries could go in the interstices). 95 | 96 | * Maybe fix PPM output. 97 | 98 | * Maybe keep a record of all merge attempts, successful and failed. 99 | 100 | * When running subprocesses, set a more specific value to environment variable `GIT_IMERGE` reflecting exactly what git-imerge is doing at the time (e.g., 'autofill', 'automerge', etc). See issue #17. 101 | 102 | * Add better tools and hints for getting out of a screwed-up merge attempt (see, e.g., issue #29). 103 | 104 | 105 | ## Testing 106 | 107 | * Add a tool that can create two branches with conflicts at arbitrary places. (This is pretty easy--if commit I1-I2 should conflict, then make commits I1 and I2 both create a file `conflict-i1-i2` with differing contents. 108 | 109 | * Test adjacent conflicts in various places. 110 | 111 | * Cook up some way to make conflicts that unexpectedly appear and disappear when merged as part of a block vs. pairwise. (Maybe this can be done using commits involving file renames followed by the addition of replacements.) Test such scenarios. 112 | 113 | 114 | ## GUI 115 | 116 | * Maybe add a web interface (implementing using Python's built-in webserver) would be easiest. 117 | -------------------------------------------------------------------------------- /completions/git-imerge: -------------------------------------------------------------------------------- 1 | __git_imerge_branches () { 2 | git for-each-ref --format='%(refname)' refs/heads/ refs/remotes/ 2>/dev/null | 3 | sed -e 's!^refs/heads/!!' -e 's!^refs/remotes/!!' 4 | } 5 | 6 | __git_imerge_names () { 7 | git for-each-ref --format='%(refname)' refs/imerge/ 2>/dev/null | 8 | sed -e 's/^refs\/imerge\/\(.*\)\/.*/\1/' -e '/manual/d' -e '/auto/d' 9 | } 10 | 11 | __git_imerge_goals="\ 12 | merge 13 | rebase 14 | rebase-with-history 15 | full 16 | " 17 | 18 | __git_imerge_commands="\ 19 | start 20 | merge 21 | rebase 22 | drop 23 | revert 24 | continue 25 | finish 26 | diagram 27 | list 28 | init 29 | record 30 | autofill 31 | simplify 32 | remove 33 | reparent 34 | " 35 | 36 | __git_imerge_start_options="\ 37 | --help 38 | --manual 39 | --name 40 | --branch 41 | --goal 42 | --first-parent 43 | " 44 | 45 | __git_imerge_init_options=$__git_imerge_start_options 46 | 47 | __git_imerge_continue_options="\ 48 | --help 49 | --name 50 | --edit 51 | --no-edit 52 | " 53 | 54 | __git_imerge_finish_options="\ 55 | --help 56 | --name 57 | --goal 58 | --branch 59 | --force 60 | " 61 | 62 | __git_imerge_simplify_options=$__git_imerge_finish_options 63 | 64 | __git_imerge_merge_options="\ 65 | --help 66 | --name 67 | --goal 68 | --branch 69 | --manual 70 | " 71 | 72 | __git_imerge_drop_options="\ 73 | --help 74 | --name 75 | --branch 76 | --manual 77 | " 78 | 79 | __git_imerge_list_options="\ 80 | --help 81 | " 82 | 83 | __git_imerge_reparent_options=$__git_imerge_list_options 84 | 85 | __git_imerge_record_options="\ 86 | --help 87 | --name 88 | --edit 89 | --no-edit 90 | " 91 | 92 | __git_imerge_autofill_options="\ 93 | --help 94 | --name 95 | " 96 | 97 | __git_imerge_diagram_options="\ 98 | --help 99 | --name 100 | --commits 101 | --frontier 102 | --html 103 | --color 104 | --no-color 105 | " 106 | 107 | __git_imerge_remove_options=$__git_imerge_autofill_options 108 | 109 | __git_imerge_rebase_options=$__git_imerge_merge_options 110 | 111 | __git_imerge_revert_options=$__git_imerge_drop_options 112 | 113 | __git-imerge_start_completion() { 114 | case "$1_$cur" in 115 | --help_|--branch_|_--branch=|--name_|_--name=) 116 | return 117 | ;; 118 | --goal_*|*_--goal=*) 119 | __gitcomp "$__git_imerge_goals" "" "${cur##--goal=}" 120 | return 121 | ;; 122 | *-|*_-*?) 123 | __gitcomp "$__git_imerge_start_options" 124 | return 125 | ;; 126 | esac 127 | __gitcomp "$(__git_imerge_branches)" 128 | } 129 | 130 | __git-imerge_init_completion() { 131 | __git-imerge_start_completion $1 132 | } 133 | 134 | __git-imerge_continue_completion() { 135 | case "$1_$cur" in 136 | --help_) 137 | return 138 | ;; 139 | --name_*|*_--name=*) 140 | __gitcomp "$(__git_imerge_names)" "" "${cur##--name=}" 141 | return 142 | ;; 143 | esac 144 | __gitcomp "$__git_imerge_continue_options" 145 | } 146 | 147 | __git-imerge_finish_completion() { 148 | case "$1_$cur" in 149 | --help_) 150 | return 151 | ;; 152 | --goal_*|*_--goal=*) 153 | __gitcomp "$__git_imerge_goals" "" "${cur##--goal=}" 154 | return 155 | ;; 156 | --branch_*|*_--branch=*) 157 | __gitcomp "$(__git_imerge_branches)" "" "${cur##--branch=}" 158 | return 159 | ;; 160 | --name_*|*_--name=*) 161 | __gitcomp "$(__git_imerge_names)" "" "${cur##--name=}" 162 | return 163 | ;; 164 | esac 165 | __gitcomp "$__git_imerge_finish_options" 166 | } 167 | 168 | __git-imerge_simplify_completion() { 169 | __git-imerge_finish_completion $1 170 | } 171 | 172 | __git-imerge_merge_completion() { 173 | case "$1_$cur" in 174 | --help_|--branch_|_--branch=|--name_|_--name=) 175 | return 176 | ;; 177 | --goal_*|*_--goal=*) 178 | __gitcomp "$__git_imerge_goals" "" "${cur##--goal=}" 179 | return 180 | ;; 181 | *-|*_-*?) 182 | __gitcomp "$__git_imerge_merge_options" 183 | return 184 | ;; 185 | esac 186 | __gitcomp "$(__git_imerge_branches)" 187 | } 188 | 189 | __git-imerge_rebase_completion() { 190 | __git-imerge_merge_completion $1 191 | } 192 | 193 | __git-imerge_drop_completion() { 194 | case "$1_$cur" in 195 | --help_|--branch_|_--branch=|--name_|_--name=) 196 | return 197 | ;; 198 | *-|*_-*?) 199 | __gitcomp "$__git_imerge_merge_options" 200 | return 201 | ;; 202 | esac 203 | __gitcomp "$(__git_imerge_branches)" 204 | } 205 | 206 | __git-imerge_revert_completion() { 207 | __git-imerge_drop_completion $1 208 | } 209 | 210 | __git-imerge_list_completion() { 211 | case "$1" in 212 | --help) 213 | return 214 | ;; 215 | esac 216 | __gitcomp "$__git_imerge_list_options" 217 | } 218 | 219 | __git-imerge_reparent_completion() { 220 | __git-imerge_list_completion $1 221 | } 222 | 223 | __git-imerge_record_completion() { 224 | case "$1_$cur" in 225 | --help_) 226 | return 227 | ;; 228 | --name_*|*_--name=*) 229 | __gitcomp "$(__git_imerge_names)" "" "${cur##--name=}" 230 | return 231 | ;; 232 | esac 233 | __gitcomp "$__git_imerge_record_options" 234 | } 235 | 236 | __git-imerge_autofill_completion() { 237 | case "$1_$cur" in 238 | --help_) 239 | return 240 | ;; 241 | --name_*|*_--name=*) 242 | __gitcomp "$(__git_imerge_names)" "" "${cur##--name=}" 243 | return 244 | ;; 245 | esac 246 | __gitcomp "$__git_imerge_autofill_options" 247 | } 248 | 249 | __git-imerge_remove_completion() { 250 | __git-imerge_autofill_completion $1 251 | } 252 | 253 | __git-imerge_diagram_completion() { 254 | case "$1_$cur" in 255 | --help_|--html_|_--html=) 256 | return 257 | ;; 258 | --name_*|*_--name=*) 259 | __gitcomp "$(__git_imerge_names)" "" "${cur##--name=}" 260 | return 261 | ;; 262 | esac 263 | __gitcomp "$__git_imerge_diagram_options" 264 | } 265 | 266 | _git_imerge () { 267 | # Disable default filename completion. Note that "compopt" is only 268 | # available in Bash 4 and newer, so we check for existence before 269 | # trying to use it. 270 | type compopt >/dev/null 2>&1 && compopt +o default +o bashdefault 271 | 272 | local i command cur_opt 273 | 274 | # find which git-imerge subcommand is on the line 275 | # For example: 276 | # words=(git imerge start master) 277 | # command="start" 278 | # 279 | # Start looking at index 1 because the logic can be simpler when 280 | # it assumes that "imerge" will always be in the position before 281 | # the subcommand. 282 | for ((i=1; i <= ${cword}; i++)); do 283 | if [ -n "$command" ] && [ "${words[i]}" != "$cur" ]; then 284 | cur_opt="${words[i]}" 285 | fi 286 | if [ -z "$command" ] && \ 287 | [ "$i" -lt "${cword}" ] && \ 288 | [[ "${words[i-1]}" == *"imerge"* ]] 289 | then 290 | command="${words[i]}" 291 | fi 292 | done 293 | 294 | if test -z "$command"; then 295 | __gitcomp "$__git_imerge_commands" 296 | return 297 | fi 298 | 299 | if [ -z `type -t __git-imerge_"$command"_completion "$cur_opt"` ]; then 300 | return 301 | fi 302 | 303 | __git-imerge_"$command"_completion "$cur_opt" 304 | } 305 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/.gitignore: -------------------------------------------------------------------------------- 1 | /talk.html 2 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/Makefile: -------------------------------------------------------------------------------- 1 | all: html 2 | 3 | html: talk.html 4 | 5 | talk.html: talk.rst 6 | rst2s5 --theme=small-white --current-slide $< $@ 7 | 8 | clean: 9 | rm -f talk.html 10 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/README.txt: -------------------------------------------------------------------------------- 1 | I gave this talk on 2013-May-10 at the GitMerge conference in Berlin 2 | on "User Day". 3 | 4 | (I gave another, more technical talk on a whiteboard the day before at 5 | "Developer Day".) 6 | 7 | To convert it into HTML, run "make html" in this directory. 8 | 9 | -- Michael Haggerty 10 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/talk.rst: -------------------------------------------------------------------------------- 1 | .. include:: 2 | 3 | .. |bullet| unicode:: U+02022 4 | 5 | .. footer:: 6 | 7 | Michael Haggerty |bullet| ``mhagger@alum.mit.edu`` |bullet| GitMerge conference 2013 8 | 9 | ========== 10 | git-imerge 11 | ========== 12 | 13 | .. class:: center 14 | 15 | ``https://github.com/mhagger/git-imerge`` 16 | 17 | **Incremental merging for Git** 18 | 19 | | "git-merge on steroids" 20 | | or 21 | | "git-rebase for the masses" 22 | 23 | | Michael Haggerty 24 | | ``mhagger@alum.mit.edu`` 25 | | ``https://softwareswirl.blogspot.com/`` 26 | 27 | Overview 28 | ======== 29 | 30 | * Why you need it 31 | 32 | * What it does 33 | 34 | * How to use it 35 | 36 | 37 | Why incremental merge? 38 | ====================== 39 | 40 | .. class:: incremental 41 | 42 | .. container:: 43 | 44 | There are two standard alternatives for combining branches: 45 | 46 | * ``git merge`` 47 | 48 | * ``git rebase`` 49 | 50 | Both have serious problems. 51 | 52 | ``git merge`` pain 53 | ================== 54 | 55 | .. class:: incremental 56 | 57 | You need to resolve **one big conflict** that mixes up a lot of little 58 | changes on both sides of the merge. 59 | 60 | (Resolving big conflicts is **hard**!) 61 | 62 | ``git merge`` pain (cont'd) 63 | =========================== 64 | 65 | .. class:: incremental 66 | 67 | Merging is **all-or-nothing**. There is **no way to save** a 68 | partly-done merge, so 69 | 70 | * You can't record your progress. 71 | 72 | * You can't switch to another branch temporarily. 73 | 74 | * If you make a mistake, you can't go back. 75 | 76 | If you cannot resolve the **whole** conflict, there is nothing to 77 | do but start over. 78 | 79 | ``git merge`` pain (cont'd) 80 | =========================== 81 | 82 | .. class:: incremental 83 | 84 | * There is **no way to test** a partly-done merge. 85 | 86 | .. class:: incremental 87 | 88 | (The code won't even build until the conflict is completely resolved.) 89 | 90 | * It is **difficult to collaborate** on a merge with a colleague. 91 | 92 | 93 | ``git rebase`` pain 94 | =================== 95 | 96 | .. class:: incremental 97 | 98 | * You have to reconcile each of the branch commits with *all* of the 99 | changes on master. 100 | 101 | * Rebasing is **all-or-nothing**; it is awkward to interrupt a rebase 102 | while it is in progress. 103 | 104 | * Rebasing is **unfriendly to collaboration**; it is problematic to 105 | rebase work that has been published. 106 | 107 | ``git rebase`` pain (cont'd) 108 | ============================ 109 | 110 | .. class:: incremental 111 | 112 | * Rebasing **discards history** (namely the old version of the 113 | branch). 114 | 115 | * Rebasing often requires similar conflicts to be **resolved multiple 116 | times**. 117 | 118 | 119 | Incremental merge 120 | ================= 121 | 122 | ``git-imerge`` implements a new merging method, **incremental merge**, 123 | that reduces the pain of merging to its essential minimum. 124 | 125 | 126 | ``git-imerge`` 127 | ============== 128 | 129 | .. class:: incremental 130 | 131 | ``git-imerge`` presents conflicts pairwise, **one commit from each 132 | branch** 133 | 134 | * Small conflicts are *much* easier to resolve than large 135 | conflicts. 136 | 137 | * You can view commit messages and individual diffs to see what 138 | each commit was trying to do. 139 | 140 | 141 | ``git-imerge`` (cont'd) 142 | ======================= 143 | 144 | .. class:: incremental 145 | 146 | ``git-imerge`` **records all intermediate merges** 147 | 148 | ...with their correct ancestry, 149 | 150 | ...as commits in your repository. 151 | 152 | Because of this, an incremental merge... 153 | 154 | * ...can be **interrupted**. 155 | 156 | * ...can be **pushed to a server**. 157 | 158 | * ...can be **pulled by a colleague**, worked on, and pushed again. 159 | 160 | 161 | ``git-imerge`` (cont'd) 162 | ======================= 163 | 164 | .. class:: incremental 165 | 166 | ``git-imerge`` **never shows the same conflict twice**. 167 | 168 | * Once a conflict has been resolved, it is stored in the DAG to 169 | make future merges easier. 170 | 171 | ``git-imerge`` lets you **test every intermediate state**. 172 | 173 | * If there is a problem, you can use ``git bisect`` to find the exact 174 | pairwise merge that was faulty. 175 | 176 | * You can redo that merge and continue the incremental merge from 177 | there (retaining earlier pairwise merges). 178 | 179 | 180 | ``git-imerge`` (cont'd) 181 | ======================= 182 | 183 | .. class:: incremental 184 | 185 | ``git-imerge`` is **largely automated** and **surprisingly efficient**. 186 | 187 | ``git-imerge`` allows the final merge to be **simplified for the 188 | permanent record**, omitting the intermediate results. 189 | 190 | 191 | The basic idea 192 | ============== 193 | 194 | Suppose you want to merge "branch" into "master":: 195 | 196 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 197 | \ 198 | A - B - C - D - E - F - G ← branch 199 | 200 | .. class:: incremental 201 | 202 | First draw the diagram a bit differently... 203 | 204 | 205 | The basic idea (cont'd) 206 | ======================= 207 | 208 | :: 209 | 210 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 211 | | 212 | A 213 | | 214 | B 215 | | 216 | C 217 | | 218 | D 219 | | 220 | E 221 | | 222 | F 223 | | 224 | G 225 | 226 | ↑ 227 | branch 228 | 229 | .. class:: incremental 230 | 231 | Now start filling it in, merging one pair at a time... 232 | 233 | 234 | The basic idea (cont'd) 235 | ======================= 236 | 237 | :: 238 | 239 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 240 | | | 241 | A - A1 242 | | 243 | B 244 | | 245 | C 246 | | 247 | D 248 | | 249 | E 250 | | 251 | F 252 | | 253 | G 254 | 255 | ↑ 256 | branch 257 | 258 | .. class:: incremental 259 | 260 | "A1" is a merge commit between commit "1" on master and commit "A" 261 | on branch. It has two parents, like any merge commit. 262 | 263 | ``git-imerge`` stores commit A1 to your repository. 264 | 265 | 266 | The basic idea (cont'd) 267 | ======================= 268 | 269 | :: 270 | 271 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 272 | | | 273 | A - A1 274 | | | 275 | B - B1 276 | | 277 | C 278 | | 279 | D 280 | | 281 | E 282 | | 283 | F 284 | | 285 | G 286 | 287 | ↑ 288 | branch 289 | 290 | .. class:: incremental 291 | 292 | B1 is a merge between A1 and B. 293 | 294 | B1 only has to add the changes from commit B to state A1... 295 | 296 | ...or equivalently, the changes from 1 into state B. 297 | 298 | 299 | The basic idea (cont'd) 300 | ======================= 301 | 302 | :: 303 | 304 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 305 | | | 306 | A - A1 307 | | | 308 | B - B1 309 | | 310 | C 311 | | 312 | D 313 | | 314 | E 315 | | 316 | F 317 | | 318 | G 319 | 320 | ↑ 321 | branch 322 | 323 | .. class:: incremental 324 | 325 | Most of these pairwise merges will not conflict, and ``git-imerge`` 326 | will do them for you automatically 327 | 328 | ...until it finds a conflict... 329 | 330 | 331 | The basic idea (cont'd) 332 | ======================= 333 | 334 | :: 335 | 336 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 337 | | | 338 | A - A1 339 | | | 340 | B - B1 341 | | | 342 | C - C1 343 | | 344 | D 345 | | 346 | E 347 | | 348 | F 349 | | 350 | G 351 | 352 | ↑ 353 | branch 354 | 355 | 356 | The basic idea (cont'd) 357 | ======================= 358 | 359 | :: 360 | 361 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 362 | | | 363 | A - A1 364 | | | 365 | B - B1 366 | | | 367 | C - C1 368 | | | 369 | D - D1 370 | | 371 | E 372 | | 373 | F 374 | | 375 | G 376 | 377 | ↑ 378 | branch 379 | 380 | 381 | The basic idea (cont'd) 382 | ======================= 383 | 384 | :: 385 | 386 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 387 | | | 388 | A - A1 389 | | | 390 | B - B1 391 | | | 392 | C - C1 393 | | | 394 | D - D1 395 | | | 396 | E - E1 397 | | 398 | F 399 | | 400 | G 401 | 402 | ↑ 403 | branch 404 | 405 | 406 | The basic idea (cont'd) 407 | ======================= 408 | 409 | :: 410 | 411 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 412 | | | 413 | A - A1 414 | | | 415 | B - B1 416 | | | 417 | C - C1 418 | | | 419 | D - D1 420 | | | 421 | E - E1 422 | | | 423 | F - F1 424 | | 425 | G 426 | 427 | ↑ 428 | branch 429 | 430 | 431 | The basic idea (cont'd) 432 | ======================= 433 | 434 | :: 435 | 436 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 437 | | | 438 | A - A1 439 | | | 440 | B - B1 441 | | | 442 | C - C1 443 | | | 444 | D - D1 445 | | | 446 | E - E1 447 | | | 448 | F - F1 449 | | | 450 | G - G1 451 | 452 | ↑ 453 | branch 454 | 455 | 456 | The basic idea (cont'd) 457 | ======================= 458 | 459 | :: 460 | 461 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 462 | | | | 463 | A - A1 - A2 464 | | | 465 | B - B1 466 | | | 467 | C - C1 468 | | | 469 | D - D1 470 | | | 471 | E - E1 472 | | | 473 | F - F1 474 | | | 475 | G - G1 476 | 477 | ↑ 478 | branch 479 | 480 | 481 | The basic idea (cont'd) 482 | ======================= 483 | 484 | :: 485 | 486 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 487 | | | | 488 | A - A1 - A2 489 | | | | 490 | B - B1 - B2 491 | | | 492 | C - C1 493 | | | 494 | D - D1 495 | | | 496 | E - E1 497 | | | 498 | F - F1 499 | | | 500 | G - G1 501 | 502 | ↑ 503 | branch 504 | 505 | 506 | The basic idea (cont'd) 507 | ======================= 508 | 509 | :: 510 | 511 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 512 | | | | 513 | A - A1 - A2 514 | | | | 515 | B - B1 - B2 516 | | | | 517 | C - C1 - C2 518 | | | 519 | D - D1 520 | | | 521 | E - E1 522 | | | 523 | F - F1 524 | | | 525 | G - G1 526 | 527 | ↑ 528 | branch 529 | 530 | .. class:: incremental 531 | 532 | Falling asleep? 533 | 534 | (No wonder; ``git-imerge`` is doing all the work.) 535 | 536 | It's time for a little quiz. 537 | 538 | 539 | The basic idea (cont'd) 540 | ======================= 541 | 542 | :: 543 | 544 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 545 | | | | 546 | A - A1 - A2 547 | | | | 548 | B - B1 - B2 549 | | | | 550 | C - C1 - C2 551 | | | 552 | D - D1 553 | | | 554 | E - E1 555 | | | 556 | F - F1 557 | | | 558 | G - G1 559 | 560 | ↑ 561 | branch 562 | 563 | .. class:: incremental 564 | 565 | What is the meaning of C2? 566 | 567 | It adds the changes from commit C to state B2... 568 | 569 | ...equivalently, it adds the changes from commit 2 to state C1. 570 | 571 | 572 | The basic idea (cont'd) 573 | ======================= 574 | 575 | :: 576 | 577 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 578 | | | | 579 | A - A1 - A2 580 | | | | 581 | B - B1 - B2 582 | | | | 583 | C - C1 - C2 584 | | | 585 | D - D1 586 | | | 587 | E - E1 588 | | | 589 | F - F1 590 | | | 591 | G - G1 592 | 593 | ↑ 594 | branch 595 | 596 | **Important take-home message**: 597 | 598 | .. class:: incremental 599 | 600 | * Each of these merges adds the changes from *one single* commit to 601 | a state that has already been committed. 602 | 603 | * Git knows a nearby common ancestor. 604 | 605 | 606 | The basic idea (cont'd) 607 | ======================= 608 | 609 | :: 610 | 611 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 612 | | | | 613 | A - A1 - A2 614 | | | | 615 | B - B1 - B2 616 | | | | 617 | C - C1 - C2 618 | | | | 619 | D - D1 - D2 620 | | | 621 | E - E1 622 | | | 623 | F - F1 624 | | | 625 | G - G1 626 | 627 | ↑ 628 | branch 629 | 630 | The basic idea (cont'd) 631 | ======================= 632 | 633 | :: 634 | 635 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 636 | | | | 637 | A - A1 - A2 638 | | | | 639 | B - B1 - B2 640 | | | | 641 | C - C1 - C2 642 | | | | 643 | D - D1 - D2 644 | | | | 645 | E - E1 - E2 646 | | | 647 | F - F1 648 | | | 649 | G - G1 650 | 651 | ↑ 652 | branch 653 | 654 | The basic idea (cont'd) 655 | ======================= 656 | 657 | :: 658 | 659 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 660 | | | | 661 | A - A1 - A2 662 | | | | 663 | B - B1 - B2 664 | | | | 665 | C - C1 - C2 666 | | | | 667 | D - D1 - D2 668 | | | | 669 | E - E1 - E2 670 | | | 671 | F - F1 X 672 | | | 673 | G - G1 674 | 675 | ↑ 676 | branch 677 | 678 | OOPS! Conflict at X. 679 | 680 | .. class:: incremental 681 | 682 | ``git-imerge`` needs your help! 683 | 684 | Please merge E2 and F1 to make a commit F2 685 | 686 | The basic idea (cont'd) 687 | ======================= 688 | 689 | :: 690 | 691 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 692 | | | | 693 | A - A1 - A2 694 | | | | 695 | B - B1 - B2 696 | | | | 697 | C - C1 - C2 698 | | | | 699 | D - D1 - D2 700 | | | | 701 | E - E1 - E2 702 | | | 703 | F - F1 X 704 | | | 705 | G - G1 706 | 707 | ↑ 708 | branch 709 | 710 | .. class:: incremental 711 | 712 | The commits share E1 as a common ancestor ("merge base"). 713 | 714 | You need to add the change made in commit F to state E2... 715 | 716 | ...or equivalently, add the change made in commit 2 to state 717 | F1. 718 | 719 | The basic idea (cont'd) 720 | ======================= 721 | 722 | :: 723 | 724 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 725 | | | | 726 | A - A1 - A2 727 | | | | 728 | B - B1 - B2 729 | | | | 730 | C - C1 - C2 731 | | | | 732 | D - D1 - D2 733 | | | | 734 | E - E1 - E2 735 | | | | 736 | F - F1 - F2 737 | | | 738 | G - G1 739 | 740 | ↑ 741 | branch 742 | 743 | .. class:: incremental 744 | 745 | Continue in this manner until the diagram is complete... 746 | 747 | The basic idea (cont'd) 748 | ======================= 749 | 750 | :: 751 | 752 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 753 | | | | | | | | | | | | | 754 | A - A1 - A2 - A3 - A4 - A5 - A6 - A7 - A8 - A9 - A10 - A11 755 | | | | | | | | | | | | | 756 | B - B1 - B2 - B3 - B4 - B5 - B6 - B7 - B8 - B9 - B10 - B11 757 | | | | | | | | | | | | | 758 | C - C1 - C2 - C3 - C4 - C5 - C6 - C7 - C8 - C9 - C10 - C11 759 | | | | | | | | | | | | | 760 | D - D1 - D2 - D3 - D4 - D5 - D6 - D7 - D8 - D9 - D10 - D11 761 | | | | | | | | | | | | | 762 | E - E1 - E2 - E3 - E4 - E5 - E6 - E7 - E8 - E9 - E10 - E11 763 | | | | | | | | | | | | | 764 | F - F1 - F2 - F3 - F4 - F5 - F6 - F7 - F8 - F9 - F10 - F11 765 | | | | | | | | | | | | | 766 | G - G1 - G2 - G3 - G4 - G5 - G6 - G7 - G8 - G9 - G10 - G11 767 | 768 | ↑ 769 | branch 770 | 771 | .. class:: incremental 772 | 773 | Done! 774 | 775 | A completed incremental merge contains all of the information you 776 | could possibly want to know about combining two branches. 777 | 778 | 779 | Simplifying the results 780 | ======================= 781 | 782 | .. class:: incremental 783 | 784 | ...in fact, it knows *too much* information. 785 | 786 | You probably want to eliminate some of the intermediate information 787 | before storing it in your project's permanent record. 788 | 789 | For example... 790 | 791 | 792 | Simplifying the results (cont'd) 793 | ================================ 794 | 795 | :: 796 | 797 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 798 | | | | | | | | | | | | | 799 | A - A1 - A2 - A3 - A4 - A5 - A6 - A7 - A8 - A9 - A10 - A11 800 | | | | | | | | | | | | | 801 | B - B1 - B2 - B3 - B4 - B5 - B6 - B7 - B8 - B9 - B10 - B11 802 | | | | | | | | | | | | | 803 | C - C1 - C2 - C3 - C4 - C5 - C6 - C7 - C8 - C9 - C10 - C11 804 | | | | | | | | | | | | | 805 | D - D1 - D2 - D3 - D4 - D5 - D6 - D7 - D8 - D9 - D10 - D11 806 | | | | | | | | | | | | | 807 | E - E1 - E2 - E3 - E4 - E5 - E6 - E7 - E8 - E9 - E10 - E11 808 | | | | | | | | | | | | | 809 | F - F1 - F2 - F3 - F4 - F5 - F6 - F7 - F8 - F9 - F10 - F11 810 | | | | | | | | | | | | | 811 | G - G1 - G2 - G3 - G4 - G5 - G6 - G7 - G8 - G9 - G10 - G11 812 | 813 | ↑ 814 | branch 815 | 816 | .. class:: incremental 817 | 818 | Q: Where is the simple merge of "branch" and "master"? 819 | 820 | A: G11 821 | 822 | 823 | Simplifying the results (cont'd) 824 | ================================ 825 | 826 | :: 827 | 828 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 829 | | | 830 | A | 831 | | | 832 | B | 833 | | | 834 | C | 835 | | | 836 | D | 837 | | | 838 | E | 839 | | | 840 | F | 841 | | | 842 | G ---------------------------------------------------- G11' ← master 843 | 844 | ↑ 845 | branch 846 | 847 | Q: Where is the simple merge of "branch" and "master"? 848 | 849 | A: G11 850 | 851 | 852 | Simplifying the results (cont'd) 853 | ================================ 854 | 855 | :: 856 | 857 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 858 | | | 859 | A | 860 | | | 861 | B | 862 | | | 863 | C | 864 | | | 865 | D | 866 | | | 867 | E | 868 | | | 869 | F | 870 | | | 871 | G ---------------------------------------------------- G11' ← master 872 | 873 | ↑ 874 | branch 875 | 876 | Usually such a diagram is drawn like so:: 877 | 878 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - G11' ← master 879 | \ / 880 | A ---- B ---- C ----- D ----- E ----- F ----- G ← branch 881 | 882 | 883 | Simplifying the results (cont'd) 884 | ================================ 885 | 886 | :: 887 | 888 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 889 | | | | | | | | | | | | | 890 | A - A1 - A2 - A3 - A4 - A5 - A6 - A7 - A8 - A9 - A10 - A11 891 | | | | | | | | | | | | | 892 | B - B1 - B2 - B3 - B4 - B5 - B6 - B7 - B8 - B9 - B10 - B11 893 | | | | | | | | | | | | | 894 | C - C1 - C2 - C3 - C4 - C5 - C6 - C7 - C8 - C9 - C10 - C11 895 | | | | | | | | | | | | | 896 | D - D1 - D2 - D3 - D4 - D5 - D6 - D7 - D8 - D9 - D10 - D11 897 | | | | | | | | | | | | | 898 | E - E1 - E2 - E3 - E4 - E5 - E6 - E7 - E8 - E9 - E10 - E11 899 | | | | | | | | | | | | | 900 | F - F1 - F2 - F3 - F4 - F5 - F6 - F7 - F8 - F9 - F10 - F11 901 | | | | | | | | | | | | | 902 | G - G1 - G2 - G3 - G4 - G5 - G6 - G7 - G8 - G9 - G10 - G11 903 | 904 | ↑ 905 | branch 906 | 907 | .. class:: incremental 908 | 909 | Q: Where is the rebase of "branch" onto "master"? 910 | 911 | A: The rightmost column... 912 | 913 | Simplifying the results (cont'd) 914 | ================================ 915 | 916 | :: 917 | 918 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 919 | | 920 | A11' 921 | | 922 | B11' 923 | | 924 | C11' 925 | | 926 | D11' 927 | | 928 | E11' 929 | | 930 | F11' 931 | | 932 | G11' ← branch 933 | 934 | Q: Where is the rebase of "branch" onto "master"? 935 | 936 | A: The rightmost column. 937 | 938 | 939 | 940 | Simplifying the results (cont'd) 941 | ================================ 942 | 943 | :: 944 | 945 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 946 | | | | | | | | | | | | | 947 | A - A1 - A2 - A3 - A4 - A5 - A6 - A7 - A8 - A9 - A10 - A11 948 | | | | | | | | | | | | | 949 | B - B1 - B2 - B3 - B4 - B5 - B6 - B7 - B8 - B9 - B10 - B11 950 | | | | | | | | | | | | | 951 | C - C1 - C2 - C3 - C4 - C5 - C6 - C7 - C8 - C9 - C10 - C11 952 | | | | | | | | | | | | | 953 | D - D1 - D2 - D3 - D4 - D5 - D6 - D7 - D8 - D9 - D10 - D11 954 | | | | | | | | | | | | | 955 | E - E1 - E2 - E3 - E4 - E5 - E6 - E7 - E8 - E9 - E10 - E11 956 | | | | | | | | | | | | | 957 | F - F1 - F2 - F3 - F4 - F5 - F6 - F7 - F8 - F9 - F10 - F11 958 | | | | | | | | | | | | | 959 | G - G1 - G2 - G3 - G4 - G5 - G6 - G7 - G8 - G9 - G10 - G11 960 | 961 | ↑ 962 | branch 963 | 964 | .. class:: incremental 965 | 966 | Q: Where is the rebase of "master" onto "branch"? 967 | 968 | A: The bottommost row... 969 | 970 | Simplifying the results (cont'd) 971 | ================================ 972 | 973 | :: 974 | 975 | o - 0 976 | | 977 | A 978 | | 979 | B 980 | | 981 | C 982 | | 983 | D 984 | | 985 | E 986 | | 987 | F 988 | | 989 | G - G1'- G2'- G3'- G4'- G5'- G6'- G7'- G8'- G9'- G10'- G11' ← master 990 | 991 | ↑ 992 | branch 993 | 994 | Q: Where is the rebase of "master" onto "branch"? 995 | 996 | A: The bottommost row. 997 | 998 | 999 | Simplifying the results (cont'd) 1000 | ================================ 1001 | 1002 | :: 1003 | 1004 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 1005 | | | | | | | | | | | | | 1006 | A - A1 - A2 - A3 - A4 - A5 - A6 - A7 - A8 - A9 - A10 - A11 1007 | | | | | | | | | | | | | 1008 | B - B1 - B2 - B3 - B4 - B5 - B6 - B7 - B8 - B9 - B10 - B11 1009 | | | | | | | | | | | | | 1010 | C - C1 - C2 - C3 - C4 - C5 - C6 - C7 - C8 - C9 - C10 - C11 1011 | | | | | | | | | | | | | 1012 | D - D1 - D2 - D3 - D4 - D5 - D6 - D7 - D8 - D9 - D10 - D11 1013 | | | | | | | | | | | | | 1014 | E - E1 - E2 - E3 - E4 - E5 - E6 - E7 - E8 - E9 - E10 - E11 1015 | | | | | | | | | | | | | 1016 | F - F1 - F2 - F3 - F4 - F5 - F6 - F7 - F8 - F9 - F10 - F11 1017 | | | | | | | | | | | | | 1018 | G - G1 - G2 - G3 - G4 - G5 - G6 - G7 - G8 - G9 - G10 - G11 1019 | 1020 | ↑ 1021 | branch 1022 | 1023 | .. class:: incremental 1024 | 1025 | If you have already published branch, consider simplifying into a 1026 | "rebase with history"... 1027 | 1028 | Simplifying the results (cont'd) 1029 | ================================ 1030 | 1031 | :: 1032 | 1033 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 1034 | | | 1035 | A ---------------------------------------------------- A11' 1036 | | | 1037 | B ---------------------------------------------------- B11' 1038 | | | 1039 | C ---------------------------------------------------- C11' 1040 | | | 1041 | D ---------------------------------------------------- D11' 1042 | | | 1043 | E ---------------------------------------------------- E11' 1044 | | | 1045 | F ---------------------------------------------------- F11' 1046 | | | 1047 | G ---------------------------------------------------- G11' 1048 | 1049 | ↑ 1050 | branch 1051 | 1052 | .. class:: incremental 1053 | 1054 | Rebase-with-history is a useful hybrid between rebasing and 1055 | merging: 1056 | 1057 | * It retains both the old and the new versions of branch. 1058 | 1059 | * It can be used even if branch has been published. 1060 | 1061 | 1062 | Efficiency 1063 | ========== 1064 | 1065 | .. class:: incremental 1066 | 1067 | In most cases ``git-imerge`` does not have to construct the full 1068 | incremental merge. 1069 | 1070 | It uses an efficient algorithm, based on bisection, for filling in 1071 | large blocks of the incremental merge quickly and homing in on the 1072 | conflicts. 1073 | 1074 | 1075 | Efficiency (cont'd) 1076 | =================== 1077 | 1078 | A typical in-progress merge might look like this:: 1079 | 1080 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 1081 | | | | | | | | | | | | | 1082 | A - A1 - A2 - A3 - A4 - A5 - A6 - A7 - A8 - A9 - A10 - A11 1083 | | | | | | | | | | 1084 | B - B1 - B2 - B3 - B4 - B5 - B6 - B7 - B8 X 1085 | | | | | | | | 1086 | C - C1 - C2 - C3 - C4 - C5 - C6 X 1087 | | | | | | | | 1088 | D - D1 - D2 - D3 - D4 - D5 - D6 1089 | | | | | | | | 1090 | E - E1 - E2 - E3 - E4 - E5 - E6 1091 | | | 1092 | F - F1 X 1093 | | | 1094 | G - G1 1095 | 1096 | ↑ 1097 | branch 1098 | 1099 | .. class:: incremental 1100 | 1101 | * The Xs marks pairwise merges that conflict 1102 | 1103 | 1104 | Efficiency (cont'd) 1105 | =================== 1106 | 1107 | But ``git-imerge`` only needs to compute this:: 1108 | 1109 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 1110 | | | | | | | | | | | | | 1111 | A - -- -- -- -- -- A6 - -- A8 - A9 - A10 - A11 1112 | | | | | | | | | | 1113 | B - -- -- -- -- -- B6 - B7 - B8 X 1114 | | | | | | | | 1115 | C - -- -- -- -- -- C6 X 1116 | | | | | | | | 1117 | D - -- -- -- -- -- D6 1118 | | | | | | | | 1119 | E - E1 - E2 - E3 - E4 - E5 - E6 1120 | | | 1121 | F - F1 X 1122 | | | 1123 | G - G1 1124 | 1125 | ↑ 1126 | branch 1127 | 1128 | .. class:: incremental 1129 | 1130 | (Plus a few test merges to locate the conflicts.) 1131 | 1132 | 1133 | Efficiency (cont'd) 1134 | =================== 1135 | 1136 | But ``git-imerge`` only needs to compute this:: 1137 | 1138 | o - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 ← master 1139 | | | | | | | | | | | | | 1140 | A - -- -- -- -- -- A6 - -- A8 - A9 - A10 - A11 1141 | | | | | | | | | | 1142 | B - -- -- -- -- -- B6 - B7 - B8 X 1143 | | | | | | | | 1144 | C - -- -- -- -- -- C6 X 1145 | | | | | | | | 1146 | D - -- -- -- -- -- D6 1147 | | | | | | | | 1148 | E - E1 - E2 - E3 - E4 - E5 - E6 1149 | | | 1150 | F - F1 X 1151 | | | 1152 | G - G1 1153 | 1154 | ↑ 1155 | branch 1156 | 1157 | .. class:: incremental 1158 | 1159 | The gaps could be skipped because the merges on the boundaries were 1160 | all successful. 1161 | 1162 | 1163 | git-imerge demo 1164 | =============== 1165 | 1166 | Time for a simple demo. 1167 | 1168 | 1169 | git-imerge demo 1170 | =============== 1171 | 1172 | Time for a simple demo. 1173 | 1174 | You start like ``git merge``:: 1175 | 1176 | $ git checkout master 1177 | $ git imerge start --name=merge-branch --first-parent branch 1178 | 1179 | .. class:: incremental 1180 | 1181 | Each imerge gets a name. 1182 | 1183 | 1184 | git-imerge demo (cont'd) 1185 | ======================== 1186 | 1187 | ``git-imerge`` uses bisection to find pairwise merges that conflict, 1188 | filling everything it can:: 1189 | 1190 | Attempting automerge of 1-1...success. 1191 | Attempting automerge of 1-4...success. 1192 | Attempting automerge of 1-6...success. 1193 | Attempting automerge of 9-6...failure. 1194 | Attempting automerge of 5-6...failure. 1195 | Attempting automerge of 3-6...success. 1196 | Attempting automerge of 4-6...failure. 1197 | Attempting automerge of 4-1...success. 1198 | Attempting automerge of 4-4...failure. 1199 | Attempting automerge of 4-3...failure. 1200 | Attempting automerge of 4-2...success. 1201 | Attempting automerge of 9-2...success. 1202 | Autofilling 1-6...success. 1203 | Autofilling 2-6...success. 1204 | Autofilling 3-1...success. 1205 | Autofilling 3-2...success. 1206 | [...] 1207 | Autofilling 3-6...success. 1208 | Autofilling 4-2...success. 1209 | [...] 1210 | Autofilling 8-2...success. 1211 | Autofilling 9-1...success. 1212 | Autofilling 9-2...success. 1213 | 1214 | 1215 | git-imerge demo (cont'd) 1216 | ======================== 1217 | 1218 | When it hits a conflict, it asks for help:: 1219 | 1220 | Attempting automerge of 4-3...failure. 1221 | Switched to branch 'imerge/merge-branch' 1222 | Auto-merging conflict.txt 1223 | CONFLICT (add/add): Merge conflict in conflict.txt 1224 | Automatic merge failed; fix conflicts and then commit the result. 1225 | 1226 | Original first commit: 1227 | commit b7fe8a65d0cee2e388e971c4b29be8c6cbb25ee1 1228 | Author: Lou User 1229 | Date: Fri May 3 14:03:05 2013 +0200 1230 | 1231 | c conflict 1232 | 1233 | Original second commit: 1234 | commit bd0373cadae08d872536bcda8214c0631e19945a 1235 | Author: Lou User 1236 | Date: Fri May 3 14:03:05 2013 +0200 1237 | 1238 | d conflict 1239 | 1240 | There was a conflict merging commit 4-3, shown above. 1241 | Please resolve the conflict, commit the result, then type 1242 | 1243 | git-imerge continue 1244 | 1245 | 1246 | git-imerge demo (cont'd) 1247 | ======================== 1248 | 1249 | You can get a diagram of the current state of the merge (it is in 1250 | crude ASCII-art for now):: 1251 | 1252 | $ git imerge diagram 1253 | ********** 1254 | *??|?????| 1255 | *--+-----+ 1256 | *??|#????? 1257 | *??|?????? 1258 | *??|?????? 1259 | *--+?????? 1260 | 1261 | Key: 1262 | |,-,+ = rectangles forming current merge frontier 1263 | * = merge done manually 1264 | . = merge done automatically 1265 | # = conflict that is currently blocking progress 1266 | @ = merge was blocked but has been resolved 1267 | ? = no merge recorded 1268 | 1269 | 1270 | git-imerge demo (cont'd) 1271 | ======================== 1272 | 1273 | You fix the conflict then tell ``git-imerge`` to continue:: 1274 | 1275 | $ git add ... 1276 | $ git commit 1277 | $ git imerge continue 1278 | Merge has been recorded for merge 4-3. 1279 | Attempting automerge of 5-3...success. 1280 | Attempting automerge of 5-3...success. 1281 | Attempting automerge of 9-3...success. 1282 | Autofilling 5-3...success. 1283 | Autofilling 6-3...success. 1284 | Autofilling 7-3...success. 1285 | Autofilling 8-3...success. 1286 | Autofilling 9-3...success. 1287 | Attempting automerge of 4-4...success. 1288 | Attempting automerge of 4-5...success. 1289 | Attempting automerge of 4-6...success. 1290 | Attempting automerge of 9-6...success. 1291 | Autofilling 4-6...success. 1292 | Autofilling 5-6...success. 1293 | Autofilling 6-6...success. 1294 | Autofilling 7-6...success. 1295 | Autofilling 8-6...success. 1296 | Autofilling 9-4...success. 1297 | Autofilling 9-5...success. 1298 | Autofilling 9-6...success. 1299 | Merge is complete! 1300 | 1301 | 1302 | git-imerge demo (cont'd) 1303 | ======================== 1304 | 1305 | Here is a diagram of the completed merge:: 1306 | 1307 | $ git imerge diagram 1308 | ********** 1309 | *??.?????| 1310 | *??......| 1311 | *??.*....| 1312 | *??.?????| 1313 | *??.?????| 1314 | *--------+ 1315 | 1316 | Key: 1317 | |,-,+ = rectangles forming current merge frontier 1318 | * = merge done manually 1319 | . = merge done automatically 1320 | # = conflict that is currently blocking progress 1321 | @ = merge was blocked but has been resolved 1322 | ? = no merge recorded 1323 | 1324 | 1325 | git-imerge demo (cont'd) 1326 | ======================== 1327 | 1328 | Now simplify the incremental merge into a simple merge, a simple 1329 | rebase, or a rebase-with-history:: 1330 | 1331 | $ git imerge finish --goal=merge 1332 | $ git log -1 --decorate 1333 | commit 79afd870a52e216114596b52c800e45139badf74 (HEAD, merge-branch) 1334 | Merge: 8453321 993a8a9 1335 | Author: Lou User 1336 | Date: Wed May 8 10:08:07 2013 +0200 1337 | 1338 | Merge frobnicator into floobifier. 1339 | 1340 | 1341 | Intermediate data 1342 | ================= 1343 | 1344 | During an incremental merge, intermediate results are stored directly 1345 | in your repository as special references under ``refs/imerge/NAME/``: 1346 | 1347 | ``refs/imerge/NAME/state`` 1348 | A blob containing a little bit of metadata. 1349 | 1350 | ``refs/imerge/NAME/{manual,auto}/M-N`` 1351 | Manual/automatic merge that includes all of the changes through 1352 | commits ``M`` on master and ``N`` on branch. 1353 | 1354 | ``refs/heads/imerge/NAME`` 1355 | Temporary branch used when resolving merge conflicts. 1356 | 1357 | ``refs/heads/NAME`` 1358 | Default branch where final results are stored. 1359 | 1360 | 1361 | Summary 1362 | ======= 1363 | 1364 | As of this writing, ``git-imerge`` is very new and still experimental. 1365 | Please try it out, but use it cautiously (e.g., on a clone of your 1366 | main Git repository). 1367 | 1368 | .. class:: incremental 1369 | 1370 | | Give me your feedback! 1371 | | Michael Haggerty 1372 | | ``mhagger@alum.mit.edu`` 1373 | 1374 | | Get involved! 1375 | | ``https://github.com/mhagger/git-imerge`` 1376 | 1377 | | For more details, see my blog "SoftwareSwirl": 1378 | | ``https://softwareswirl.blogspot.com/`` 1379 | 1380 | Thank you for your attention! 1381 | 1382 | 1383 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/ui/small-white/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mhagger/git-imerge/9bde208ce9d33e65d1c5930be676f6725f4d3d4b/doc/presentations/GitMerge-2013/ui/small-white/blank.gif -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/ui/small-white/framing.css: -------------------------------------------------------------------------------- 1 | /* This file has been placed in the public domain. */ 2 | /* The following styles size, place, and layer the slide components. 3 | Edit these if you want to change the overall slide layout. 4 | The commented lines can be uncommented (and modified, if necessary) 5 | to help you with the rearrangement process. */ 6 | 7 | /* target = 1024x768 */ 8 | 9 | div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} 10 | div#footer {top: auto; bottom: 0; height: 2.5em; z-index: 5;} 11 | .slide {top: 0; width: 92%; padding: 1em 4% 0 4%; z-index: 2;} 12 | div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} 13 | div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; 14 | margin: 0;} 15 | #currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; 16 | z-index: 10;} 17 | html>body #currentSlide {position: fixed;} 18 | 19 | /* 20 | div#header {background: #FCC;} 21 | div#footer {background: #CCF;} 22 | div#controls {background: #BBD;} 23 | div#currentSlide {background: #FFC;} 24 | */ 25 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/ui/small-white/iepngfix.htc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 42 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/ui/small-white/opera.css: -------------------------------------------------------------------------------- 1 | /* This file has been placed in the public domain. */ 2 | /* DO NOT CHANGE THESE unless you really want to break Opera Show */ 3 | .slide { 4 | visibility: visible !important; 5 | position: static !important; 6 | page-break-before: always; 7 | } 8 | #slide0 {page-break-before: avoid;} 9 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/ui/small-white/outline.css: -------------------------------------------------------------------------------- 1 | /* This file has been placed in the public domain. */ 2 | /* Don't change this unless you want the layout stuff to show up in the 3 | outline view! */ 4 | 5 | .layout div, #footer *, #controlForm * {display: none;} 6 | #footer, #controls, #controlForm, #navLinks, #toggle { 7 | display: block; visibility: visible; margin: 0; padding: 0;} 8 | #toggle {float: right; padding: 0.5em;} 9 | html>body #toggle {position: fixed; top: 0; right: 0;} 10 | 11 | /* making the outline look pretty-ish */ 12 | 13 | #slide0 h1, #slide0 h2, #slide0 h3, #slide0 h4 {border: none; margin: 0;} 14 | #toggle {border: 1px solid; border-width: 0 0 1px 1px; background: #FFF;} 15 | 16 | .outline {display: inline ! important;} 17 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/ui/small-white/pretty.css: -------------------------------------------------------------------------------- 1 | /* This file has been placed in the public domain. */ 2 | /* Following are the presentation styles -- edit away! */ 3 | 4 | html, body {margin: 0; padding: 0;} 5 | body {background: white; color: black;} 6 | :link, :visited {text-decoration: none; color: #00C;} 7 | #controls :active {color: #888 !important;} 8 | #controls :focus {outline: 1px dotted #222;} 9 | h1, h2, h3, h4 {font-size: 100%; margin: 0; padding: 0; font-weight: inherit;} 10 | 11 | blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em;} 12 | blockquote p {margin: 0;} 13 | 14 | kbd {font-weight: bold; font-size: 1em;} 15 | sup {font-size: smaller; line-height: 1px;} 16 | 17 | .slide pre {padding: 0; margin-left: 0; margin-right: 0; font-size: 90%;} 18 | .slide ul ul li {list-style: square;} 19 | .slide img.leader {display: block; margin: 0 auto;} 20 | .slide tt {font-size: 90%;} 21 | 22 | div#footer {font-family: sans-serif; color: #444; 23 | font-size: 0.5em; font-weight: bold; padding: 1em 0;} 24 | #footer h1 {display: block; padding: 0 1em;} 25 | #footer h2 {display: block; padding: 0.8em 1em 0;} 26 | 27 | .slide {font-size: 1.2em;} 28 | .slide h1 {padding-top: 0; z-index: 1; margin: 0; font: bold 150% sans-serif;} 29 | .slide h2 {font: bold 120% sans-serif; padding-top: 0.5em;} 30 | .slide h3 {font: bold 100% sans-serif; padding-top: 0.5em;} 31 | h1 abbr {font-variant: small-caps;} 32 | 33 | div#controls {position: absolute; left: 50%; bottom: 0; 34 | width: 50%; text-align: right; font: bold 0.9em sans-serif;} 35 | html>body div#controls {position: fixed; padding: 0 0 1em 0; top: auto;} 36 | div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; 37 | margin: 0; padding: 0;} 38 | #controls #navLinks a {padding: 0; margin: 0 0.5em; 39 | border: none; color: #888; cursor: pointer;} 40 | #controls #navList {height: 1em;} 41 | #controls #navList #jumplist {position: absolute; bottom: 0; right: 0; 42 | background: #DDD; color: #222;} 43 | 44 | #currentSlide {text-align: center; font-size: 0.5em; color: #444; 45 | font-family: sans-serif; font-weight: bold;} 46 | 47 | #slide0 {padding-top: 0em} 48 | #slide0 h1 {position: static; margin: 1em 0 0; padding: 0; 49 | font: bold 2em sans-serif; white-space: normal; background: transparent;} 50 | #slide0 h2 {font: bold italic 1em sans-serif; margin: 0.25em;} 51 | #slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} 52 | #slide0 h4 {margin-top: 0; font-size: 1em;} 53 | 54 | ul.urls {list-style: none; display: inline; margin: 0;} 55 | .urls li {display: inline; margin: 0;} 56 | .external {border-bottom: 1px dotted gray;} 57 | html>body .external {border-bottom: none;} 58 | .external:after {content: " \274F"; font-size: smaller; color: #77B;} 59 | 60 | .incremental, .incremental *, .incremental *:after { 61 | color: white; visibility: visible; border: 0; border: 0;} 62 | img.incremental {visibility: hidden;} 63 | .slide .current {color: green;} 64 | 65 | .slide-display {display: inline ! important;} 66 | 67 | .huge {font-family: sans-serif; font-weight: bold; font-size: 150%;} 68 | .big {font-family: sans-serif; font-weight: bold; font-size: 120%;} 69 | .small {font-size: 75%;} 70 | .tiny {font-size: 50%;} 71 | .huge tt, .big tt, .small tt, .tiny tt {font-size: 115%;} 72 | .huge pre, .big pre, .small pre, .tiny pre {font-size: 115%;} 73 | 74 | .maroon {color: maroon;} 75 | .red {color: red;} 76 | .magenta {color: magenta;} 77 | .fuchsia {color: fuchsia;} 78 | .pink {color: #FAA;} 79 | .orange {color: orange;} 80 | .yellow {color: yellow;} 81 | .lime {color: lime;} 82 | .green {color: green;} 83 | .olive {color: olive;} 84 | .teal {color: teal;} 85 | .cyan {color: cyan;} 86 | .aqua {color: aqua;} 87 | .blue {color: blue;} 88 | .navy {color: navy;} 89 | .purple {color: purple;} 90 | .black {color: black;} 91 | .gray {color: gray;} 92 | .silver {color: silver;} 93 | .white {color: white;} 94 | 95 | .left {text-align: left ! important;} 96 | .center {text-align: center ! important;} 97 | .right {text-align: right ! important;} 98 | 99 | .animation {position: relative; margin: 1em 0; padding: 0;} 100 | .animation img {position: absolute;} 101 | 102 | /* Docutils-specific overrides */ 103 | 104 | .slide table.docinfo {margin: 1em 0 0.5em 2em;} 105 | 106 | pre.literal-block, pre.doctest-block {background-color: #EEE; font-size: 50%;} 107 | 108 | tt.docutils {background-color: white;} 109 | 110 | /* diagnostics */ 111 | /* 112 | li:after {content: " [" attr(class) "]"; color: #F88;} 113 | div:before {content: "[" attr(class) "]"; color: #F88;} 114 | */ 115 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/ui/small-white/print.css: -------------------------------------------------------------------------------- 1 | /* This file has been placed in the public domain. */ 2 | /* The following rule is necessary to have all slides appear in print! 3 | DO NOT REMOVE IT! */ 4 | .slide, ul {page-break-inside: avoid; visibility: visible !important;} 5 | h1 {page-break-after: avoid;} 6 | 7 | body {font-size: 12pt; background: white;} 8 | * {color: black;} 9 | 10 | #slide0 h1 {font-size: 200%; border: none; margin: 0.5em 0 0.25em;} 11 | #slide0 h3 {margin: 0; padding: 0;} 12 | #slide0 h4 {margin: 0 0 0.5em; padding: 0;} 13 | #slide0 {margin-bottom: 3em;} 14 | 15 | #header {display: none;} 16 | #footer h1 {margin: 0; border-bottom: 1px solid; color: gray; 17 | font-style: italic;} 18 | #footer h2, #controls {display: none;} 19 | 20 | .print {display: inline ! important;} 21 | 22 | /* The following rule keeps the layout stuff out of print. 23 | Remove at your own risk! */ 24 | .layout, .layout * {display: none !important;} 25 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/ui/small-white/s5-core.css: -------------------------------------------------------------------------------- 1 | /* This file has been placed in the public domain. */ 2 | /* Do not edit or override these styles! 3 | The system will likely break if you do. */ 4 | 5 | div#header, div#footer, div#controls, .slide {position: absolute;} 6 | html>body div#header, html>body div#footer, 7 | html>body div#controls, html>body .slide {position: fixed;} 8 | .handout {display: none;} 9 | .layout {display: block;} 10 | .slide, .hideme, .incremental {visibility: hidden;} 11 | #slide0 {visibility: visible;} 12 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/ui/small-white/slides.css: -------------------------------------------------------------------------------- 1 | /* This file has been placed in the public domain. */ 2 | 3 | /* required to make the slide show run at all */ 4 | @import url(s5-core.css); 5 | 6 | /* sets basic placement and size of slide components */ 7 | @import url(framing.css); 8 | 9 | /* styles that make the slides look good */ 10 | @import url(pretty.css); 11 | -------------------------------------------------------------------------------- /doc/presentations/GitMerge-2013/ui/small-white/slides.js: -------------------------------------------------------------------------------- 1 | // S5 v1.1 slides.js -- released into the Public Domain 2 | // Modified for Docutils (http://docutils.sf.net) by David Goodger 3 | // 4 | // Please see http://www.meyerweb.com/eric/tools/s5/credits.html for 5 | // information about all the wonderful and talented contributors to this code! 6 | 7 | var undef; 8 | var slideCSS = ''; 9 | var snum = 0; 10 | var smax = 1; 11 | var slideIDs = new Array(); 12 | var incpos = 0; 13 | var number = undef; 14 | var s5mode = true; 15 | var defaultView = 'slideshow'; 16 | var controlVis = 'visible'; 17 | 18 | var isIE = navigator.appName == 'Microsoft Internet Explorer' ? 1 : 0; 19 | var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0; 20 | var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0; 21 | 22 | function hasClass(object, className) { 23 | if (!object.className) return false; 24 | return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1); 25 | } 26 | 27 | function hasValue(object, value) { 28 | if (!object) return false; 29 | return (object.search('(^|\\s)' + value + '(\\s|$)') != -1); 30 | } 31 | 32 | function removeClass(object,className) { 33 | if (!object) return; 34 | object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2); 35 | } 36 | 37 | function addClass(object,className) { 38 | if (!object || hasClass(object, className)) return; 39 | if (object.className) { 40 | object.className += ' '+className; 41 | } else { 42 | object.className = className; 43 | } 44 | } 45 | 46 | function GetElementsWithClassName(elementName,className) { 47 | var allElements = document.getElementsByTagName(elementName); 48 | var elemColl = new Array(); 49 | for (var i = 0; i< allElements.length; i++) { 50 | if (hasClass(allElements[i], className)) { 51 | elemColl[elemColl.length] = allElements[i]; 52 | } 53 | } 54 | return elemColl; 55 | } 56 | 57 | function isParentOrSelf(element, id) { 58 | if (element == null || element.nodeName=='BODY') return false; 59 | else if (element.id == id) return true; 60 | else return isParentOrSelf(element.parentNode, id); 61 | } 62 | 63 | function nodeValue(node) { 64 | var result = ""; 65 | if (node.nodeType == 1) { 66 | var children = node.childNodes; 67 | for (var i = 0; i < children.length; ++i) { 68 | result += nodeValue(children[i]); 69 | } 70 | } 71 | else if (node.nodeType == 3) { 72 | result = node.nodeValue; 73 | } 74 | return(result); 75 | } 76 | 77 | function slideLabel() { 78 | var slideColl = GetElementsWithClassName('*','slide'); 79 | var list = document.getElementById('jumplist'); 80 | smax = slideColl.length; 81 | for (var n = 0; n < smax; n++) { 82 | var obj = slideColl[n]; 83 | 84 | var did = 'slide' + n.toString(); 85 | if (obj.getAttribute('id')) { 86 | slideIDs[n] = obj.getAttribute('id'); 87 | } 88 | else { 89 | obj.setAttribute('id',did); 90 | slideIDs[n] = did; 91 | } 92 | if (isOp) continue; 93 | 94 | var otext = ''; 95 | var menu = obj.firstChild; 96 | if (!menu) continue; // to cope with empty slides 97 | while (menu && menu.nodeType == 3) { 98 | menu = menu.nextSibling; 99 | } 100 | if (!menu) continue; // to cope with slides with only text nodes 101 | 102 | var menunodes = menu.childNodes; 103 | for (var o = 0; o < menunodes.length; o++) { 104 | otext += nodeValue(menunodes[o]); 105 | } 106 | list.options[list.length] = new Option(n + ' : ' + otext, n); 107 | } 108 | } 109 | 110 | function currentSlide() { 111 | var cs; 112 | var footer_nodes; 113 | var vis = 'visible'; 114 | if (document.getElementById) { 115 | cs = document.getElementById('currentSlide'); 116 | footer_nodes = document.getElementById('footer').childNodes; 117 | } else { 118 | cs = document.currentSlide; 119 | footer = document.footer.childNodes; 120 | } 121 | cs.innerHTML = '' + snum + '<\/span> ' + 122 | '\/<\/span> ' + 123 | '' + (smax-1) + '<\/span>'; 124 | if (snum == 0) { 125 | vis = 'hidden'; 126 | } 127 | cs.style.visibility = vis; 128 | for (var i = 0; i < footer_nodes.length; i++) { 129 | if (footer_nodes[i].nodeType == 1) { 130 | footer_nodes[i].style.visibility = vis; 131 | } 132 | } 133 | } 134 | 135 | function go(step) { 136 | if (document.getElementById('slideProj').disabled || step == 0) return; 137 | var jl = document.getElementById('jumplist'); 138 | var cid = slideIDs[snum]; 139 | var ce = document.getElementById(cid); 140 | if (incrementals[snum].length > 0) { 141 | for (var i = 0; i < incrementals[snum].length; i++) { 142 | removeClass(incrementals[snum][i], 'current'); 143 | removeClass(incrementals[snum][i], 'incremental'); 144 | } 145 | } 146 | if (step != 'j') { 147 | snum += step; 148 | lmax = smax - 1; 149 | if (snum > lmax) snum = lmax; 150 | if (snum < 0) snum = 0; 151 | } else 152 | snum = parseInt(jl.value); 153 | var nid = slideIDs[snum]; 154 | var ne = document.getElementById(nid); 155 | if (!ne) { 156 | ne = document.getElementById(slideIDs[0]); 157 | snum = 0; 158 | } 159 | if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;} 160 | if (incrementals[snum].length > 0 && incpos == 0) { 161 | for (var i = 0; i < incrementals[snum].length; i++) { 162 | if (hasClass(incrementals[snum][i], 'current')) 163 | incpos = i + 1; 164 | else 165 | addClass(incrementals[snum][i], 'incremental'); 166 | } 167 | } 168 | if (incrementals[snum].length > 0 && incpos > 0) 169 | addClass(incrementals[snum][incpos - 1], 'current'); 170 | ce.style.visibility = 'hidden'; 171 | ne.style.visibility = 'visible'; 172 | jl.selectedIndex = snum; 173 | currentSlide(); 174 | number = 0; 175 | } 176 | 177 | function goTo(target) { 178 | if (target >= smax || target == snum) return; 179 | go(target - snum); 180 | } 181 | 182 | function subgo(step) { 183 | if (step > 0) { 184 | removeClass(incrementals[snum][incpos - 1],'current'); 185 | removeClass(incrementals[snum][incpos], 'incremental'); 186 | addClass(incrementals[snum][incpos],'current'); 187 | incpos++; 188 | } else { 189 | incpos--; 190 | removeClass(incrementals[snum][incpos],'current'); 191 | addClass(incrementals[snum][incpos], 'incremental'); 192 | addClass(incrementals[snum][incpos - 1],'current'); 193 | } 194 | } 195 | 196 | function toggle() { 197 | var slideColl = GetElementsWithClassName('*','slide'); 198 | var slides = document.getElementById('slideProj'); 199 | var outline = document.getElementById('outlineStyle'); 200 | if (!slides.disabled) { 201 | slides.disabled = true; 202 | outline.disabled = false; 203 | s5mode = false; 204 | fontSize('1em'); 205 | for (var n = 0; n < smax; n++) { 206 | var slide = slideColl[n]; 207 | slide.style.visibility = 'visible'; 208 | } 209 | } else { 210 | slides.disabled = false; 211 | outline.disabled = true; 212 | s5mode = true; 213 | fontScale(); 214 | for (var n = 0; n < smax; n++) { 215 | var slide = slideColl[n]; 216 | slide.style.visibility = 'hidden'; 217 | } 218 | slideColl[snum].style.visibility = 'visible'; 219 | } 220 | } 221 | 222 | function showHide(action) { 223 | var obj = GetElementsWithClassName('*','hideme')[0]; 224 | switch (action) { 225 | case 's': obj.style.visibility = 'visible'; break; 226 | case 'h': obj.style.visibility = 'hidden'; break; 227 | case 'k': 228 | if (obj.style.visibility != 'visible') { 229 | obj.style.visibility = 'visible'; 230 | } else { 231 | obj.style.visibility = 'hidden'; 232 | } 233 | break; 234 | } 235 | } 236 | 237 | // 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/) 238 | function keys(key) { 239 | if (!key) { 240 | key = event; 241 | key.which = key.keyCode; 242 | } 243 | if (key.which == 84) { 244 | toggle(); 245 | return; 246 | } 247 | if (s5mode) { 248 | switch (key.which) { 249 | case 10: // return 250 | case 13: // enter 251 | if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; 252 | if (key.target && isParentOrSelf(key.target, 'controls')) return; 253 | if(number != undef) { 254 | goTo(number); 255 | break; 256 | } 257 | case 32: // spacebar 258 | case 34: // page down 259 | case 39: // rightkey 260 | case 40: // downkey 261 | if(number != undef) { 262 | go(number); 263 | } else if (!incrementals[snum] || incpos >= incrementals[snum].length) { 264 | go(1); 265 | } else { 266 | subgo(1); 267 | } 268 | break; 269 | case 33: // page up 270 | case 37: // leftkey 271 | case 38: // upkey 272 | if(number != undef) { 273 | go(-1 * number); 274 | } else if (!incrementals[snum] || incpos <= 0) { 275 | go(-1); 276 | } else { 277 | subgo(-1); 278 | } 279 | break; 280 | case 36: // home 281 | goTo(0); 282 | break; 283 | case 35: // end 284 | goTo(smax-1); 285 | break; 286 | case 67: // c 287 | showHide('k'); 288 | break; 289 | } 290 | if (key.which < 48 || key.which > 57) { 291 | number = undef; 292 | } else { 293 | if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; 294 | if (key.target && isParentOrSelf(key.target, 'controls')) return; 295 | number = (((number != undef) ? number : 0) * 10) + (key.which - 48); 296 | } 297 | } 298 | return false; 299 | } 300 | 301 | function clicker(e) { 302 | number = undef; 303 | var target; 304 | if (window.event) { 305 | target = window.event.srcElement; 306 | e = window.event; 307 | } else target = e.target; 308 | if (target.href != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target, 'object')) return true; 309 | if (!e.which || e.which == 1) { 310 | if (!incrementals[snum] || incpos >= incrementals[snum].length) { 311 | go(1); 312 | } else { 313 | subgo(1); 314 | } 315 | } 316 | } 317 | 318 | function findSlide(hash) { 319 | var target = document.getElementById(hash); 320 | if (target) { 321 | for (var i = 0; i < slideIDs.length; i++) { 322 | if (target.id == slideIDs[i]) return i; 323 | } 324 | } 325 | return null; 326 | } 327 | 328 | function slideJump() { 329 | if (window.location.hash == null || window.location.hash == '') { 330 | currentSlide(); 331 | return; 332 | } 333 | if (window.location.hash == null) return; 334 | var dest = null; 335 | dest = findSlide(window.location.hash.slice(1)); 336 | if (dest == null) { 337 | dest = 0; 338 | } 339 | go(dest - snum); 340 | } 341 | 342 | function fixLinks() { 343 | var thisUri = window.location.href; 344 | thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length); 345 | var aelements = document.getElementsByTagName('A'); 346 | for (var i = 0; i < aelements.length; i++) { 347 | var a = aelements[i].href; 348 | var slideID = a.match('\#.+'); 349 | if ((slideID) && (slideID[0].slice(0,1) == '#')) { 350 | var dest = findSlide(slideID[0].slice(1)); 351 | if (dest != null) { 352 | if (aelements[i].addEventListener) { 353 | aelements[i].addEventListener("click", new Function("e", 354 | "if (document.getElementById('slideProj').disabled) return;" + 355 | "go("+dest+" - snum); " + 356 | "if (e.preventDefault) e.preventDefault();"), true); 357 | } else if (aelements[i].attachEvent) { 358 | aelements[i].attachEvent("onclick", new Function("", 359 | "if (document.getElementById('slideProj').disabled) return;" + 360 | "go("+dest+" - snum); " + 361 | "event.returnValue = false;")); 362 | } 363 | } 364 | } 365 | } 366 | } 367 | 368 | function externalLinks() { 369 | if (!document.getElementsByTagName) return; 370 | var anchors = document.getElementsByTagName('a'); 371 | for (var i=0; i' + 391 | '