class: center, middle # 10 Git Problems Git Merge 2019 Briana Swift @brianamarie ??? Notes for the _first_ slide! --- class: middle, center ## https://github.com/brianamarie/10-git-problems --- # 10 Git Problems 1. Commit is too big 2. Accidental commit to `master` 3. I'm in a detached head state 4. What broke my code? 5. I want to merge two repositories 6. I want to undo a recursive merge (without GitHub) 7. Forget a binary file 8. How do I remove a submodule? 9. I want to undo a rebase 10. How do I force git pull to overwrite local files? --- ### Assumptions - Basic Git knowledge - Enough to be dangerous ### Things to know 1. This will use `git reset`, so you should first check that: - The commits in question are _only local_ - There are no unintended files in your working or staging directory 2. `git lol` is set for every exercise 3. Most activities start by CD'ing into the directory --- ### Commit is too big > That commit is too big. It should have been broken up into several commits. 1. Change directories into the activity file: `cd 01-committed-too-big` 2. Run `. setup.sh` 3. Look at the files and the history: `ls` and `git lol` 3. `git reset reset-here` to a certain commit, bringing the changes that should be broken up into the working directory 4. Use `git add -p` to be specific about how commits are formed. You can use `s` to split even further. 5. When you're done with the activity, run `. reset.sh` and `cd ..` ??? 1: 01-committed-too-bit --- ### Accidental Commit > I committed to master instead of a branch! 1. Change directories into the activity file: `cd 02-accidental-commit-to-master` 2. Run `. setup.sh` 3. Use `git lol` and `git status` to see the current state of your repository 4. Reset back to the last commit that was meant to be on master: `git reset reset-here` - Alternatively, you could reset to `origin/master` also 5. Use `git lol` and `git status` again 6. Create a new branch and checkout to it with `git checkout -b experiment` - Notice your working and staging area will come with `HEAD` 7. Stage your changes, and make a commit 8. Once you're finished, run `. reset.sh` and `cd ..` ??? 2: 02-accidental-commit-to-master --- ### I'm in a detached head state > Git says I'm in a "detached head state" and I don't know what to do. A detached head state only means that your `HEAD` pointer is currently pointed _not_ to a branch. To fix this, to get your `HEAD` attached, you need to point to a branch. You can do this by checking out to an existing branch, or creating a new branch where you are. 1. Change directories into the activity file: `cd 03-detached-head` 2. Run `. setup.sh` 3. See where your `HEAD` is pointing with `git lol` 4. Let's create a new branch where we are with `git checkout -b more-work` - Now, you're no longer in a detached head state, and can continue working normally on your new branch 5. Reset the activity with `. reset.sh` and `cd ..` ??? 3: 03-detached-head --- ### What broke my code? > Something's been broken, but I don't know what commit broke it. There's a test in this directory, checking if any file is empty or not. This only _finds_ the commit introducing the break, it doesn't fix it at all. 1. Change directories into the activity file: `cd 04-what-broke-this` 2. See the test with `ls`, and try it with `. test.sh` 3. To get set up, run the scripts `. setup.sh` 4. Use the test `. test.sh` to find if something's broken 5. Before you can use bisect, you need to be in the root directory of the repository: `cd ..` 6. Try the test again: `. 04-what-broke-this/test.sh` 7. Begin the bisect: `git bisect start` 8. Tell Git where the "broken" commit is: `git bisect bad HEAD` 9. Tell Git where the last known "good" commit was: `git bisect good before-activity4` 10. After Git moves your `HEAD` pointer to a different commit, try your test again: `04-what-broke-this/test.sh` 11. Depending on the results of the test, type `git bisect good` or `git bisect good` 12. Continue until Git finds the commit 13. Exit the bisect: `git bisect reset` 14. Reset the files: `. 04-what-broke-this/reset.sh` ??? 4: 04-what-broke-this --- ### I want to merge two repositories > There are two separate project repositories that should be in one repository, but they have unrelated histories. In practice, you might want to create a new repository to have the combined histories of both of the other repositories. Then, it would be best to archive the singular repositories. 1. Run the setup script: `. setup.sh` 2. Add the other repository as a second remote: `git remote add other-repo https://github.com/brianamarie/another-repository.git` OR `. add-remote.sh` 3. Before you try to merge, make sure you have all of the remote tracking branches for all remotes: `git fetch --all` 4. Try to merge the two repositories: `git merge other-repo/master` and notice the error message. 5. Since these two repositories have unrelated histories, we need to specify a particular type of merge: `git merge other-repo/master --allow-unrelated-histories` 6. You can see the combined history with the alias `git lol` 7. Reset the exercise: `. reset.sh` and `cd ..` ??? 5: 05-merge-two-repos --- ### I want to undo a recursive merge (without GitHub) > A merge happened locally, and then I pushed. I'd like to revert the merge and maintain all of committed history. When reverting a merge, Git wants to know about the "parent" commits. The branch you were on when you merged is the parent, so we will refer to it as "1". 1. Run `. setup.sh` 2. Look at the log, and take lineage of commits' parents into account: `git lol` 3. Identify which branch is the "parent" branch of the merge, `md-files` in our case 4. Identify the merge commit 5. Revert the merge commit: `git revert -m
` - ex: `git revert -m 1 cd4370f` 6. Look at the files and the log: `git lol`, and `ls` 7. Reset the activity: `. reset.sh` and `cd ..` ??? 6: 06-revert-merge --- ### I need Git to ignore a binary file > I committed a binary file a long time ago, how to make git forget about a file that was tracked but is now in .gitignore? When files are defined in the `.gitignore` from the start, Git will always ignore them. But, if a file is already tracked by Git, adding the name to the `.gitignore` doesn't do any good. 1. Run `. setup.sh`, then notice your history and working directory 2. Create a `.gitignore` file in the _root directory_ of this repository 3. Add `test.img` to the `.gitignore` file 4. Add and commit the `.gitignore` file 4. Change directories back into this activity: `cd 07-forget-binary` 4. Change the big binary file `. change-binary.sh` 5. Add and commit the binary file. - What did you expect to happen? Git knows the file based on its similarity index, not by the name, so it's still tracked. 6. For Git to ignore a file that is already being tracked, you need to remove the cache: `git rm --cached test.img` > Note: this does not remove the file from history, only from tracking and any _new_ commits. ??? 7: 07-forget-binary --- ### How do I remove a submodule? > I had needed a submodule in the past, but it's no longer necessary. How do I remove it completely from this repository? Submodules are sticky business, and if you want out, it takes a few steps. 1. Make sure you're in the root directory of this repository. 2. Add a submodule with the script: `. 08-remove-submodule/setup.sh` 3. Notice there is a submodule with: `cat .gitmodules` 4. Remove the contents of the directory and "de-initialize" the submodule: `git submodule deinit example-submodule` 5. Remove the path that Git uses to keep track of the submodule: `git rm
` 6. Commit those changes: `git commit -m "removed submodule"` 6. Remove the reference in the `.git` directory: `rm -rf .git/modules/
` 7. Remove the `.gitmodules` directory from the root directory of the repository: `rm -rf .gitmodules` 8. Reset the repository: `. 08-remove-submodule/reset.sh` ??? 8: 08-remove-submodule --- ### I want to undo a rebase > I rebased onto the wrong branch! A rebase is done to force a fast-forward merge, and result in a straight line of history. To go back, you usually need to use `git reflog`. It's not intuitive or easy to find the correct commit to reset to. 1. Change directories for this activity: `cd 09-undo-rebase` 2. Run `. setup.sh` - This script creates new commits on master and on the `md-files` branch, then rebases the `md-files` branch onto `master` 3. See the history with `git lol` - Notice a tag? The script also tagged the commit _before_ the rebase took place. 4. Using the `git reflog`, identify the most recent commit by message, and find the last instance of it in the reflog 5. Use that commit ID or the tag to reset the branch: `git reset --hard before-rebase` 6. See your history, as it was before the reset: `git lol` 7. Reset the activity: `. reset.sh` and `cd ..` ??? 9: 09-undo-rebase --- ### Can I force pull from the remote? > I've made changes that seemingly conflict with the upstream changes. I don't want to keep mine, I just want to keep those from the remote! Can I force pull? While there _is_ a `git push --force` command, there isn't a `git pull --force` command. 1. Change directories for this activity: `cd 10-force-pull` 2. Run `. setup.sh` 3. Make a major change to `10-force-pull/conflicts.md` (do NOT add or commit) 4. Try to pull: what happens? 5. Three options: 1. Add and commit your changes, then `git pull` 2. `git stash`, then pull, and then `git stash pop` 3. Take the changes on master and overwrite your own: `git reset --hard origin/master` ??? 10: 10-force-pull