Cleaner [[git]] History *********************** #HTML# Mark Jason Dominus ++++++++++++++++++ Plover Systems Co. ++++++++++++++++++ mjd@plover.com v0.1 (Dec, 2013) ++++++++++++++++ ###Slides at ### ### http://perl.plover.com/yak/cs/ ---------------------------------------------------------------- [[Git]] ******* * One of git's strengths is that has good tools for tracking development history * Logging is fast and complete * We are not taking advantage of this * Git is tracking history * But our history is so complicated nobody can understand it ---------------------------------------------------------------- #IMG# tangled.png ---------------------------------------------------------------- * Here it is in another format, produced by git log --graph --oneline #IMG# tangled-text.png ---------------------------------------------------------------- * If you have been thinking "Yeah, this sucks", I agree * If you thought it was git's fault, I disagree * I think it's because we are doing it wrong ---------------------------------------------------------------- * This is what I think it should look like #IMG# untangled.png ---------------------------------------------------------------- * I think this will be easy to fix: #HTML#

#HTML# #HTML# Stop merging changes from master #HTML#

---------------------------------------------------------------- Stop merging changes from [[master]] ************************************ #IMG# tangled-hi.png ---------------------------------------------------------------- Stop merging changes from [[master]] ************************************ * You may not know you are doing this * The problem is in [[git-pull]] #HTML#

#HTML# #HTML# git-pull = git-fetch + git-merge #HTML#

* [R[Please stop using git pull]R] ---------------------------------------------------------------- Stop merging changes from [[master]] ************************************ #HTML#

#HTML# #HTML# git-pull = git-fetch + git-merge #HTML#

* Stop using [[git-pull]] * Just use [[git-fetch]] exactly the same way * It gets the same arguments * It fetches the same things * But it doesn't do a merge ---------------------------------------------------------------- Stop merging changes from [[master]] ************************************ * Develop your topic branch separate from [[master]] #IMG# history1.png * [[git-fetch]] will acquire changes from [[master as before]] * It just won't merge them to whatever your current branch happens to be #IMG# history2.png ---------------------------------------------------------------- * When the topic branch is ready, "land" it on [[master]]: git checkout master git merge topic * This automatically generates a commit message like "Merge branch 'topic' #IMG# history3.png * If you don't like that, you can: git merge [C[-m 'This is a better message']C] topic * Or, after the commit is made, modify the commit message with: git commit --amend ---------------------------------------------------------------- Stop merging changes from [[master]] ************************************ #HTML#

“But what if my code won't work with the current [[master]]?”

* There are several good ways to deal with that * But it depending on what you want * Which means you have to know what you want ---------------------------------------------------------------- Undo the merge ************** * If you don't like the outcome of the merge, it's easy to undo * Suppose you've done this: git checkout master git merge topic #IMG# history3.png * You can always throw away the most recent commit with: git reset --hard HEAD^ * After that we're back to this: #IMG# history2.png * Then you can checkout [[topic]] and continue from there as before ---------------------------------------------------------------- [[rebase]] ********** * A better way to get the new code from [[master]] into your topic branch is with [[rebase]] * [[git-rebase]] will try to redo all your changes starting from somewhere else git checkout topic git rebase origin/master * The result: #HTML# #HTML# #HTML#
Before After #HTML#
#HTML#    #HTML# #HTML#
---------------------------------------------------------------- [[rebase]] ********** * What is going on here? #IMG# history2.png * Git figures out the place your topic branch diverged from [[master]] * Makes patches for all the changes on your branch since then * Applies them in order to the code on [[origin/master]] * If it all works, [[topic]] to the head of the new topic branch #IMG# history4.png * Now your topic branch is built atop an up-to-date [[master]] ---------------------------------------------------------------- [[rebase]] ********** #HTML#

“That's too scary. What if I don't like what it does?”

* No problem * Git means never having to say "Oh, crap, I screwed up" * [C[git reset --hard ORIG_HEAD]C] will put [[topic]] back where it was ---------------------------------------------------------------- [[rebase]] ********** * Alternative: Instead of rebasing [[topic]], try this: git branch topic-rebased topic git checkout topic-rebased git rebase master #IMG# history5.png * Now you can compare [[topic]] and [[topic-rebased]] to see if you like the result * Shortcut: #* git checkout -b topic-rebased git rebase master ---------------------------------------------------------------- [[git-vee]] *********** * I have a script for comparing branches with the rebased versions #IMG# history5.png * The command [[git vee topic topic-rebased]] produces this: #HTML#
#HTML#         = e03d8a2 (topic-rebased) more changes to files named a
#HTML#         = 2713271 more a files
#HTML#         = 95536a3 add some files named a
#HTML#         * c34f8b9 (master) replace c with c2 and c3
#HTML#         * 769e6ad work on b files
#HTML#         | = 2802301 (HEAD, topic) more changes to files named a
#HTML#         | = 4cf89e7 more a files
#HTML#         | = 1c1bb18 add some files named a
#HTML#         |/
#HTML#         o 2f9d24e add a, b, and c
#HTML# 
* It shows the V-shaped history of both branches * back to the latest common ancestor * [C[=]C] marks commits that are the same in both branches * [C[*]C] marks commits that are not in both branches * It's in [[~mjd/bin/git-vee]] on dev if you want to try it ---------------------------------------------------------------- [[rebase]] ********** #HTML#

“But what if the patches don't apply?”

* Then you have to resolve a conflicted merge * You would have had to do that with [[git-pull]] anyway * After the conflicts are resolved and commited, you use: git rebase --continue ---------------------------------------------------------------- [[rebase]] ********** #HTML#

“But what if I mess everything up?”

* Once the rebase is finished you can still undo it * If you haven't done another rebase since then: git reset --hard ORIG_HEAD * If you're still in the middle of the rebase and you want to give up: git rebase --abort * That puts everything back the way it was as if you had never tried [[rebase]] * Then you can call for help * I will be glad to help * I bet Ryan will be too * Other volunteers? ---------------------------------------------------------------- [[git-pull --rebase]] ******************** #HTML#

“But git-pull is convenient and simple!”

#HTML#

“I don't want to change my workflow and do separate fetch and merge!”

* No worries! The problem is with the merges. * If you do [[git-pull --rebase]] it will do fetch + rebase instead of fetch + merge. * To set this up to happen automatically, run this one time: git config --global --add 'pull.rebase' 'true' * That adds to your `.gitconfig`: [pull] rebase = true ---------------------------------------------------------------- Merging [[master]] anyway ************************* * "But I really do want to merge [[master]]" * That's fine with me, do what you like * My only objection is to *unintentional* merges #HTML#

#HTML# #HTML# Just stop using git-pull all the time #HTML#

* That's all I'm asking for ---------------------------------------------------------------- #RTIMG# sen.jpg Thank you! ********** * Any questions? ---------------------------------------------------------------- ----------------------------------------------------------------END