<jupyter_start><jupyter_text>Git Grandmastery > Summary By now, you should be able to navigate the *git help* pages and understandalmost everything. However, pinpointing the exact command required to solve agiven problem can be tedious. Perhaps I can save you some time: below are somerecipes I have needed in the past. Source Releases For my projects, Git tracks exactly the files I'd like to archive and releaseto users. To create a tarball of the source code, I run:<jupyter_code>! git archive --format=tar --prefix=proj-1.2.3/ HEAD<jupyter_output><empty_output><jupyter_text>Commit What Changed Telling Git when you've added, deleted and renamed files is troublesome forcertain projects. Instead, you can type:<jupyter_code>! git add .
 ! git add -u<jupyter_output><empty_output><jupyter_text>Git will look at the files in the current directory and work out the details byitself. Instead of the second add command, run `git commit -a` if you alsointend to commit at this time. See *git help ignore* for how to specify filesthat should be ignored.You can perform the above in a single pass with:<jupyter_code>! git ls-files -d -m -o -z | xargs -0 git update-index --add --remove<jupyter_output><empty_output><jupyter_text>The *-z* and *-0* options prevent ill side-effects from filenames containingstrange characters. As this command adds ignored files, you may want to use the`-x` or `-X` option. My Commit Is Too Big! Have you neglected to commit for too long? Been coding furiously and forgottenabout source control until now? Made a series of unrelated changes, becausethat's your style?No worries. Run:<jupyter_code>! git add -p<jupyter_output><empty_output><jupyter_text>For each edit you made, Git will show you the hunk of code that was changed,and ask if it should be part of the next commit. Answer with "y" or "n". Youhave other options, such as postponing the decision; type "?" to learn more.Once you're satisfied, type<jupyter_code>! git commit<jupyter_output><empty_output><jupyter_text>to commit precisely the changes you selected (the 'staged' changes). Make sureyou omit the *-a* option, otherwise Git will commit all the edits.What if you've edited many files in many places? Reviewing each change one byone becomes frustratingly mind-numbing. In this case, use *git add -i*, whoseinterface is less straightforward, but more flexible. With a few keystrokes,you can stage or unstage several files at a time, or review and select changesin particular files only. Alternatively, run *git commit \--interactive* whichautomatically commits after you're done. The Index: Git's Staging Area So far we have avoided Git's famous 'index', but we must now confront it toexplain the above. The index is a temporary staging area. Git seldom shuttlesdata directly between your project and its history. Rather, Git first writesdata to the index, and then copies the data in the index to its finaldestination.For example, *commit -a* is really a two-step process. The first step places asnapshot of the current state of every tracked file into the index. The secondstep permanently records the snapshot now in the index. Committing without the*-a* option only performs the second step, and only makes sense after runningcommands that somehow change the index, such as *git add*.Usually we can ignore the index and pretend we are reading straight from and writing straight to the history. On this occasion, we want finer control, so we manipulate the index. We place a snapshot of some, but not all, of our changes into the index, and then permanently record this carefully rigged snapshot. Don't Lose Your HEAD The HEAD tag is like a cursor that normally points at the latest commit, advancing with each new commit. Some Git commands let you move it. For example:<jupyter_code>! git reset HEAD~3<jupyter_output><empty_output><jupyter_text>will move the HEAD three commits back. Thus all Git commands now act as if you hadn't made those last three commits, while your files remain in the present. See the help page for some applications.But how can you go back to the future? The past commits know nothing of the future.If you have the SHA1 of the original HEAD then:<jupyter_code>! git reset 1b6d<jupyter_output><empty_output><jupyter_text>But suppose you never took it down? Don't worry: for commands like these, Git saves the original HEAD as a tag called ORIG_HEAD, and you can return safe and sound with:<jupyter_code>! git reset ORIG_HEAD<jupyter_output><empty_output><jupyter_text>HEAD-hunting Perhaps ORIG_HEAD isn't enough. Perhaps you've just realized you made a monumental mistake and you need to go back to an ancient commit in a long-forgotten branch.By default, Git keeps a commit for at least two weeks, even if you orderedGit to destroy the branch containing it. The trouble is finding the appropriatehash. You could look at all the hash values in `.git/objects` and use trialand error to find the one you want. But there's a much easier way.Git records every hash of a commit it computes in `.git/logs`. The subdirectory `refs` contains the history of all activity on all branches, while the file `HEAD` shows every hash value it has ever taken. The latter can be used to find hashes of commits on branches that have been accidentally lopped off.The reflog command provides a friendly interface to these log files. Try<jupyter_code>! git reflog<jupyter_output><empty_output><jupyter_text>Instead of cutting and pasting hashes from the reflog, try:<jupyter_code>! git checkout "@{10 minutes ago}"<jupyter_output><empty_output><jupyter_text>Or checkout the 5th-last visited commit via:<jupyter_code>! git checkout "@{5}"<jupyter_output><empty_output><jupyter_text>See the ``Specifying Revisions'' section of *git help rev-parse* for more.You may wish to configure a longer grace period for doomed commits. Forexample:<jupyter_code>! git config gc.pruneexpire "30 days"<jupyter_output><empty_output><jupyter_text>means a deleted commit will only be permanently lost once 30 days have passedand *git gc* is run.You may also wish to disable automatic invocations of *git gc*:<jupyter_code>! git config gc.auto 0<jupyter_output><empty_output><jupyter_text>in which case commits will only be deleted when you run *git gc* manually. Building On Git In true UNIX fashion, Git's design allows it to be easily used as a low-level component of other programs, such as GUI and web interfaces, alternative command-line interfaces, patch managements tools, importing and conversion tools and so on. In fact, some Git commands are themselves scripts standing on the shoulders of giants. With a little tinkering, you can customize Git to suit your preferences.One easy trick is to use built-in Git aliases to shorten your most frequentlyused commands:<jupyter_code>! git config --global alias.co checkout
  ! git config --global --get-regexp alias  # display current aliases<jupyter_output><empty_output><jupyter_text>alias.co checkout<jupyter_code>! git co foo                              # same as 'git checkout foo'<jupyter_output><empty_output><jupyter_text>Another is to print the current branch in the prompt, or window title.Invoking<jupyter_code>! git symbolic-ref HEAD<jupyter_output><empty_output><jupyter_text>shows the current branch name. In practice, you most likely want to removethe "refs/heads/" and ignore errors:<jupyter_code>! git symbolic-ref HEAD 2> /dev/null | cut -b 12-<jupyter_output><empty_output><jupyter_text>The +contrib+ subdirectory is a treasure trove of tools built on Git.In time, some of them may be promoted to official commands. On Debian andUbuntu, this directory lives at +/usr/share/doc/git-core/contrib+.One popular resident is +workdir/git-new-workdir+. Via clever symlinking, this script creates a new working directory whose history is shared with the original repository:<jupyter_code>! git-new-workdir an/existing/repo new/directory<jupyter_output><empty_output><jupyter_text>The new directory and the files within can be thought of as a clone, except since the history is shared, the two trees automatically stay in sync. There's no need to merge, push, or pull. Daring Stunts These days, Git makes it difficult for the user to accidentally destroy data.But if you know what you are doing, you can override safeguards for commoncommands.*Checkout*: Uncommitted changes cause checkout to fail. To destroy your changes, and checkout a given commit anyway, use the force flag:<jupyter_code>! git checkout -f HEAD^<jupyter_output><empty_output><jupyter_text>On the other hand, if you specify particular paths for checkout, then there are no safety checks. The supplied paths are quietly overwritten. Take care if you use checkout in this manner.*Reset*: Reset also fails in the presence of uncommitted changes. To force it through, run:<jupyter_code>! git reset --hard 1b6d<jupyter_output><empty_output><jupyter_text>*Branch*: Deleting branches fails if this causes changes to be lost. To force a deletion, type:<jupyter_code>! git branch -D dead_branch  # instead of -d<jupyter_output><empty_output><jupyter_text>Similarly, attempting to overwrite a branch via a move fails if data loss would ensue. To force a branch move, type:<jupyter_code>! git branch -M source target  # instead of -m<jupyter_output><empty_output><jupyter_text>Unlike checkout and reset, these two commands defer data destruction. Thechanges are still stored in the .git subdirectory, and can be retrieved byrecovering the appropriate hash from `.git/logs` (see "HEAD-hunting" above).By default, they will be kept for at least two weeks.*Clean*: Some git commands refuse to proceed because they're worried aboutclobbering untracked files. If you're certain that all untracked files anddirectories are expendable, then delete them mercilessly with:<jupyter_code>! git clean -f -d<jupyter_output><empty_output><jupyter_text>Next time, that pesky command will work! Preventing Bad Commits Stupid mistakes pollute my repositories. Most frightening are missing files dueto a forgotten *git add*. Lesser transgressions are trailing whitespace andunresolved merge conflicts: though harmless, I wish these never appeared on thepublic record.If only I had bought idiot insurance by using a _hook_ to alert me about these problems:<jupyter_code>! cd .git/hooks
 ! cp pre-commit.sample pre-commit  # Older Git versions: chmod +x pre-commit<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.06136210384356035, 'fraction_numerical': 0.002215586167035931, 'mean_word_length': 5.303582270795386, 'pattern_counts': {'":': 0, '<': 97, '<?xml version=': 0, '>': 99, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '24', 'dir': 'jupyter-structured-clean-dedup', 'id': '231536', 'license': 'Apache-2.0', 'n_tokens_mistral': 2900, 'n_tokens_neox': 2776, 'n_words': 1419, 'path': '07_grandmaster.ipynb', 'repo_name': 'StephanRempel/git_magic', 'size': '10381'}