Repository: k88hudson/git-flight-rules
Branch: master
Commit: 80d27e63e154
Files: 14
Total size: 597.7 KB
Directory structure:
gitextract_i6mlkrch/
├── .gitignore
├── .travis.yml
├── CONTRIBUTE.md
├── LICENSE
├── README.md
├── README_es.md
├── README_fr.md
├── README_ja.md
├── README_kr.md
├── README_ru.md
├── README_vi.md
├── README_zh-CN.md
├── README_zh-TW.md
└── package.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules/
package-lock.json
================================================
FILE: .travis.yml
================================================
language: ruby
rvm:
- 2.2
before_script:
- gem install awesome_bot
- npm install
script:
- awesome_bot README.md --allow-dupe --allow-redirect --allow-ssl
- awesome_bot README_*.md --allow-dupe --allow-redirect --allow-ssl
- npm run toc
- npm run diff
================================================
FILE: CONTRIBUTE.md
================================================
# Contributing
This is a community built document. Dive in and help out!
### Issues
- Please jump in and help us out by making PRs for open issues.
- If you see anything you'd think could be improved, feel free to open an issue. This doesn't just mean for new additions you'd like to see - although those are very appreciated, too.
### Pull Requests
- Please title your pull requests appropriately, summing up what your commits are about.
- Make sure that your submission doesn't exist somewhere else in the document.
- If you can, run `doctoc README.md` before adding a new section, or after editing a section - this keeps the Table of Contents up to date. If you don't have it, you can install [DocToc here](https://github.com/thlorenz/doctoc).
- Include the relevant branch before the `$` in a shell command, if needed. If not needed, please do not include a branch name.
- Please label code blocks as `sh` to help out [linguist](https://github.com/github/linguist), unless the language is different.
- Try and put relevant flight rules in the same general area of the document - deletes with deletes, commit amends with commit amends, and so on. We're working on making this better, so no worries if it is confusing - just throw the submission on the end.
- Link to places you found answers or procedures if especially complicated, or if linking to a larger discussion of how to do something. This normally means linking to Stack Overflow.
- Generally, conform to this schema:
# Example Title
Brief description of the problem.
```sh
(relevant branch)$ git blah blah
```
## Translation
We use a translation tool called [GitLocalize][gl]. Please follow these steps to get started with your contributions:
1. Go to [GitLocalize's k88hudson/git-flight-rules repository][gl-repo].
1. Sign up using your GitHub account :octocat:.
1. Find the document you are going to translate.
1. Happy translating :sparkles: .
1. When you are done, send the translation for reviews.
1. The reviewed translation will be sent as a Pull Request to GitHub by the language moderator in the community.
To learn more about how GitLocalize works, visit their [help page][gl-help]. If you find any issues or feature requests, please file them in [GitLocalize's issue tracker][gl-issue-tracker].
[gl]: https://gitlocalize.com
[gl-help]: https://docs.gitlocalize.com/
[gl-issue-tracker]: https://github.com/gitlocalize/feedback
[gl-repo]: https://gitlocalize.com/repo/598
================================================
FILE: LICENSE
================================================
Attribution-ShareAlike 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More_considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution-ShareAlike 4.0 International Public
License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-ShareAlike 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. BY-SA Compatible License means a license listed at
creativecommons.org/compatiblelicenses, approved by Creative
Commons as essentially the equivalent of this Public License.
d. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
e. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
g. License Elements means the license attributes listed in the name
of a Creative Commons Public License. The License Elements of this
Public License are Attribution and ShareAlike.
h. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
i. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
k. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
l. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
m. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. Additional offer from the Licensor -- Adapted Material.
Every recipient of Adapted Material from You
automatically receives an offer from the Licensor to
exercise the Licensed Rights in the Adapted Material
under the conditions of the Adapter's License You apply.
c. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
b. ShareAlike.
In addition to the conditions in Section 3(a), if You Share
Adapted Material You produce, the following conditions also apply.
1. The Adapter's License You apply must be a Creative Commons
license with the same License Elements, this version or
later, or a BY-SA Compatible License.
2. You must include the text of, or the URI or hyperlink to, the
Adapter's License You apply. You may satisfy this condition
in any reasonable manner based on the medium, means, and
context in which You Share Adapted Material.
3. You may not offer or impose any additional or different terms
or conditions on, or apply any Effective Technological
Measures to, Adapted Material that restrict exercise of the
rights granted under the Adapter's License You apply.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material,
including for purposes of Section 3(b); and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.
Creative Commons may be contacted at creativecommons.org.
================================================
FILE: README.md
================================================
# Flight rules for Git
🌍
*[English](README.md) ∙ [Español](README_es.md) ∙ [Русский](README_ru.md) ∙ [繁體中文](README_zh-TW.md) ∙ [简体中文](README_zh-CN.md) ∙ [한국어](README_kr.md) ∙ [Tiếng Việt](README_vi.md) ∙ [Français](README_fr.md) ∙ [日本語](README_ja.md)*
#### What are "flight rules"?
A guide for astronauts (now, programmers using Git) about what to do when things go wrong.
> *Flight Rules* are the hard-earned body of knowledge recorded in manuals that list, step-by-step, what to do if X occurs, and why. Essentially, they are extremely detailed, scenario-specific standard operating procedures. [...]
> NASA has been capturing our missteps, disasters and solutions since the early 1960s, when Mercury-era ground teams first started gathering "lessons learned" into a compendium that now lists thousands of problematic situations, from engine failure to busted hatch handles to computer glitches, and their solutions.
— Chris Hadfield, *An Astronaut's Guide to Life on Earth*.
#### Conventions for this document
For clarity's sake all examples in this document use a customized bash prompt in order to indicate the current branch and whether or not there are staged changes. The branch is enclosed in parentheses, and a `*` next to the branch name indicates staged changes.
All commands should work for at least git version 2.13.0. See the [git website](https://www.git-scm.com/) to update your local git version.
[](https://gitter.im/k88hudson/git-flight-rules?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Repositories](#repositories)
- [I want to start a local repository](#i-want-to-start-a-local-repository)
- [I want to clone a remote repository](#i-want-to-clone-a-remote-repository)
- [I set the wrong remote repository](#i-set-the-wrong-remote-repository)
- [I want to add code to someone else's repository](#i-want-to-add-code-to-someone-elses-repository)
- [Suggesting code via pull requests](#suggesting-code-via-pull-requests)
- [Suggesting code via patches](#suggesting-code-via-patches)
- [I need to update my fork with latest updates from the original repository](#i-need-to-update-my-fork-with-latest-updates-from-the-original-repository)
- [Editing Commits](#editing-commits)
- [What did I just commit?](#what-did-i-just-commit)
- [I wrote the wrong thing in a commit message](#i-wrote-the-wrong-thing-in-a-commit-message)
- [I committed with the wrong name and email configured](#i-committed-with-the-wrong-name-and-email-configured)
- [I want to remove a file from the previous commit](#i-want-to-remove-a-file-from-the-previous-commit)
- [I want to move a change from one commit to another](#i-want-to-move-a-change-from-one-commit-to-another)
- [I want to delete or remove my last commit](#i-want-to-delete-or-remove-my-last-commit)
- [Delete/remove arbitrary commit](#deleteremove-arbitrary-commit)
- [I tried to push my amended commit to a remote, but I got an error message](#i-tried-to-push-my-amended-commit-to-a-remote-but-i-got-an-error-message)
- [I accidentally did a hard reset, and I want my changes back](#i-accidentally-did-a-hard-reset-and-i-want-my-changes-back)
- [I accidentally committed and pushed a merge](#i-accidentally-committed-and-pushed-a-merge)
- [I accidentally committed and pushed files containing sensitive data](#i-accidentally-committed-and-pushed-files-containing-sensitive-data)
- [I want to remove a large file from ever existing in repo history](#i-want-to-remove-a-large-file-from-ever-existing-in-repo-history)
- [Recommended Technique: Use third-party bfg](#recommended-technique-use-third-party-bfg)
- [Built-in Technique: Use git-filter-branch](#built-in-technique-use-git-filter-branch)
- [Final Step: Pushing your changed repo history](#final-step-pushing-your-changed-repo-history)
- [I need to change the content of a commit which is not my last](#i-need-to-change-the-content-of-a-commit-which-is-not-my-last)
- [Staging](#staging)
- [I want to stage all tracked files and leave untracked files](#i-want-to-stage-all-tracked-files-and-leave-untracked-files)
- [To stage part of tracked files](#to-stage-part-of-tracked-files)
- [I need to add staged changes to the previous commit](#i-need-to-add-staged-changes-to-the-previous-commit)
- [I want to stage part of a new file, but not the whole file](#i-want-to-stage-part-of-a-new-file-but-not-the-whole-file)
- [I want to add changes in one file to two different commits](#i-want-to-add-changes-in-one-file-to-two-different-commits)
- [I staged too many edits, and I want to break them out into a separate commit](#i-staged-too-many-edits-and-i-want-to-break-them-out-into-a-separate-commit)
- [I want to stage my unstaged edits, and unstage my staged edits](#i-want-to-stage-my-unstaged-edits-and-unstage-my-staged-edits)
- [I want to unstage a specific staged file](#i-want-to-unstage-a-specific-staged-file)
- [Discarding changes](#discarding-changes)
- [I want to discard my local uncommitted changes (staged and unstaged)](#i-want-to-discard-my-local-uncommitted-changes-staged-and-unstaged)
- [I want to discard specific unstaged changes](#i-want-to-discard-specific-unstaged-changes)
- [I want to discard specific unstaged files](#i-want-to-discard-specific-unstaged-files)
- [I want to discard only my unstaged local changes](#i-want-to-discard-only-my-unstaged-local-changes)
- [I want to discard all of my untracked files](#i-want-to-discard-all-of-my-untracked-files)
- [Branches](#branches)
- [I want to list all branches](#i-want-to-list-all-branches)
- [Create a branch from a commit](#create-a-branch-from-a-commit)
- [I pulled from/into the wrong branch](#i-pulled-frominto-the-wrong-branch)
- [I want to discard local commits so my branch is the same as one on the server](#i-want-to-discard-local-commits-so-my-branch-is-the-same-as-one-on-the-server)
- [I want to move my unstaged edits to a new branch](#i-want-to-move-my-unstaged-edits-to-a-new-branch)
- [I want to move my unstaged edits to a different, existing branch](#i-want-to-move-my-unstaged-edits-to-a-different-existing-branch)
- [I committed to main instead of a new branch](#i-committed-to-main-instead-of-a-new-branch)
- [I want to keep the whole file from another ref-ish](#i-want-to-keep-the-whole-file-from-another-ref-ish)
- [I made several commits on a single branch that should be on different branches](#i-made-several-commits-on-a-single-branch-that-should-be-on-different-branches)
- [I want to delete local branches that were deleted upstream](#i-want-to-delete-local-branches-that-were-deleted-upstream)
- [I accidentally deleted my branch](#i-accidentally-deleted-my-branch)
- [I want to delete a branch](#i-want-to-delete-a-branch)
- [I want to delete multiple branches](#i-want-to-delete-multiple-branches)
- [I want to rename a branch](#i-want-to-rename-a-branch)
- [I want to checkout to a remote branch that someone else is working on](#i-want-to-checkout-to-a-remote-branch-that-someone-else-is-working-on)
- [I want to create a new remote branch from current local one](#i-want-to-create-a-new-remote-branch-from-current-local-one)
- [I want to set a remote branch as the upstream for a local branch](#i-want-to-set-a-remote-branch-as-the-upstream-for-a-local-branch)
- [I want to set my HEAD to track the default remote branch](#i-want-to-set-my-head-to-track-the-default-remote-branch)
- [I made changes on the wrong branch](#i-made-changes-on-the-wrong-branch)
- [I want to split a branch into two](#i-want-to-split-a-branch-into-two)
- [Rebasing and Merging](#rebasing-and-merging)
- [I want to undo rebase/merge](#i-want-to-undo-rebasemerge)
- [I rebased, but I don't want to force push](#i-rebased-but-i-dont-want-to-force-push)
- [I need to combine commits](#i-need-to-combine-commits)
- [Safe merging strategy](#safe-merging-strategy)
- [I need to merge a branch into a single commit](#i-need-to-merge-a-branch-into-a-single-commit)
- [I want to combine only unpushed commits](#i-want-to-combine-only-unpushed-commits)
- [I need to abort the merge](#i-need-to-abort-the-merge)
- [I need to update the parent commit of my branch](#i-need-to-update-the-parent-commit-of-my-branch)
- [Check if all commits on a branch are merged](#check-if-all-commits-on-a-branch-are-merged)
- [Possible issues with interactive rebases](#possible-issues-with-interactive-rebases)
- [The rebase editing screen says 'noop'](#the-rebase-editing-screen-says-noop)
- [There were conflicts](#there-were-conflicts)
- [Stash](#stash)
- [Stash all edits](#stash-all-edits)
- [Stash specific files](#stash-specific-files)
- [Stash with message](#stash-with-message)
- [Apply a specific stash from list](#apply-a-specific-stash-from-list)
- [Stash while keeping unstaged edits](#stash-while-keeping-unstaged-edits)
- [Finding](#finding)
- [I want to find a string in any commit](#i-want-to-find-a-string-in-any-commit)
- [I want to find by author/committer](#i-want-to-find-by-authorcommitter)
- [I want to list commits containing specific files](#i-want-to-list-commits-containing-specific-files)
- [I want to view the commit history for a specific function](#i-want-to-view-the-commit-history-for-a-specific-function)
- [Find a tag where a commit is referenced](#find-a-tag-where-a-commit-is-referenced)
- [Submodules](#submodules)
- [Clone all submodules](#clone-all-submodules)
- [Remove a submodule](#remove-a-submodule)
- [Miscellaneous Objects](#miscellaneous-objects)
- [Copy a folder or file from one branch to another](#copy-a-folder-or-file-from-one-branch-to-another)
- [Restore a deleted file](#restore-a-deleted-file)
- [Delete tag](#delete-tag)
- [Recover a deleted tag](#recover-a-deleted-tag)
- [Deleted Patch](#deleted-patch)
- [Exporting a repository as a Zip file](#exporting-a-repository-as-a-zip-file)
- [Push a branch and a tag that have the same name](#push-a-branch-and-a-tag-that-have-the-same-name)
- [Tracking Files](#tracking-files)
- [I want to change a file name's capitalization, without changing the contents of the file](#i-want-to-change-a-file-names-capitalization-without-changing-the-contents-of-the-file)
- [I want to overwrite local files when doing a git pull](#i-want-to-overwrite-local-files-when-doing-a-git-pull)
- [I want to remove a file from Git but keep the file](#i-want-to-remove-a-file-from-git-but-keep-the-file)
- [I want to revert a file to a specific revision](#i-want-to-revert-a-file-to-a-specific-revision)
- [I want to list changes of a specific file between commits or branches](#i-want-to-list-changes-of-a-specific-file-between-commits-or-branches)
- [I want Git to ignore changes to a specific file](#i-want-git-to-ignore-changes-to-a-specific-file)
- [Debugging with Git](#debugging-with-git)
- [Configuration](#configuration)
- [I want to add aliases for some Git commands](#i-want-to-add-aliases-for-some-git-commands)
- [I want to add an empty directory to my repository](#i-want-to-add-an-empty-directory-to-my-repository)
- [I want to cache a username and password for a repository](#i-want-to-cache-a-username-and-password-for-a-repository)
- [I want to make Git ignore permissions and filemode changes](#i-want-to-make-git-ignore-permissions-and-filemode-changes)
- [I want to set a global user](#i-want-to-set-a-global-user)
- [I've no idea what I did wrong](#ive-no-idea-what-i-did-wrong)
- [Git Shortcuts](#git-shortcuts)
- [Git Bash](#git-bash)
- [PowerShell on Windows](#powershell-on-windows)
- [Other Resources](#other-resources)
- [Books](#books)
- [Tutorials](#tutorials)
- [Scripts and Tools](#scripts-and-tools)
- [GUI Clients](#gui-clients)
## Repositories
### I want to start a local repository
To initialize an existing directory as a Git repository:
```sh
(my-folder) $ git init
```
### I want to clone a remote repository
To clone (copy) a remote repository, copy the URL for the repository, and run:
```sh
$ git clone [url]
```
This will save it to a folder named the same as the remote repository's. Make sure you have a connection to the remote server you are cloning from (for most purposes this means making sure you are connected to the internet).
To clone it into a folder with a different name than the default repository name:
```sh
$ git clone [url] name-of-new-folder
```
### I set the wrong remote repository
There are a few possible problems here:
If you cloned the wrong repository, simply delete the directory created after running `git clone` and clone the correct repository.
If you set the wrong repository as the origin of an existing local repository, change the URL of your origin by running:
```sh
$ git remote set-url origin [url of the actual repo]
```
For more, see [this StackOverflow topic](https://stackoverflow.com/questions/2432764/how-to-change-the-uri-url-for-a-remote-git-repository#2432799).
### I want to add code to someone else's repository
Git doesn't allow you to add code to someone else's repository without access rights. Neither does GitHub, which is not the same as Git, but rather a hosted service for Git repositories. However, you can suggest code using patches, or, on GitHub, forks and pull requests.
First, a bit about forking. A fork is a copy of a repository. It is not a git operation, but is a common action on GitHub, Bitbucket, GitLab — or anywhere people host Git repositories. You can fork a repository through the hosted UI.
#### Suggesting code via pull requests
After you've forked a repository, you normally need to clone the repository to your machine. You can do some small edits on GitHub, for instance, without cloning, but this isn't a github-flight-rules list, so let's go with how to do this locally.
```sh
# if you are using ssh
$ git clone git@github.com:k88hudson/git-flight-rules.git
# if you are using https
$ git clone https://github.com/k88hudson/git-flight-rules.git
```
If you `cd` into the resulting directory, and type `git remote`, you'll see a list of the remotes. Normally there will be one remote - `origin` - which will point to `k88hudson/git-flight-rules`. In this case, we also want a remote that will point to your fork.
First, to follow a Git convention, we normally use the remote name `origin` for your own repository and `upstream` for whatever you've forked. So, rename the `origin` remote to `upstream`
```sh
$ git remote rename origin upstream
```
You can also do this using `git remote set-url`, but it takes longer and is more steps.
Then, set up a new remote that points to your project.
```sh
$ git remote add origin git@github.com:YourName/git-flight-rules.git
```
Note that now you have two remotes.
- `origin` references your own repository.
- `upstream` references the original one.
From origin, you can read and write. From upstream, you can only read.
When you've finished making whatever changes you like, push your changes (normally in a branch) to the remote named `origin`. If you're on a branch, you could use `--set-upstream` to avoid specifying the remote tracking branch on every future push using this branch. For instance:
```sh
$ (feature/my-feature) git push --set-upstream origin feature/my-feature
```
There is no way to suggest a pull request using the CLI using Git (although there are tools, like [hub](http://github.com/github/hub), which will do this for you). So, if you're ready to make a pull request, go to your GitHub (or another Git host) and create a new pull request. Note that your host automatically links the original and forked repositories.
After all of this, do not forget to respond to any code review feedback.
#### Suggesting code via patches
Another approach to suggesting code changes that doesn't rely on third party sites such as Github is to use `git format-patch`.
`format-patch` creates a .patch file for one or more commits. This file is essentially a list of changes that looks similar to the commit diffs you can view on Github.
A patch can be viewed and even edited by the recipient and applied using `git am`.
For example, to create a patch based on the previous commit you would run `git format-patch HEAD^` which would create a .patch file called something like 0001-My-Commit-Message.patch.
To apply this patch file to your repository you would run `git am ./0001-My-Commit-Message.patch`.
Patches can also be sent via email using the `git send-email` command. For information on usage and configuration see: https://git-send-email.io
#### I need to update my fork with latest updates from the original repository
After a while, the `upstream` repository may have been updated, and these updates need to be pulled into your `origin` repo. Remember that like you, other people are contributing too. Suppose that you are in your own feature branch and you need to update it with the original repository updates.
You probably have set up a remote that points to the original project. If not, do this now. Generally we use `upstream` as a remote name:
```sh
$ (main) git remote add upstream
# $ (main) git remote add upstream git@github.com:k88hudson/git-flight-rules.git
```
Now you can fetch from upstream and get the latest updates.
```sh
$ (main) git fetch upstream
$ (main) git merge upstream/main
# or using a single command
$ (main) git pull upstream main
```
## Editing Commits
### What did I just commit?
Let's say that you just blindly committed changes with `git commit -a` and you're not sure what the actual content of the commit you just made was. You can show the latest commit on your current HEAD with:
```sh
(main)$ git show
```
Or
```sh
$ git log -n1 -p
```
If you want to see a file at a specific commit, you can also do this (where `` is the commit you're interested in):
```sh
$ git show :filename
```
### I wrote the wrong thing in a commit message
If you wrote the wrong thing and the commit has not yet been pushed, you can do the following to change the commit message without changing the changes in the commit:
```sh
$ git commit --amend --only
```
This will open your default text editor, where you can edit the message. On the other hand, you can do this all in one command:
```sh
$ git commit --amend --only -m 'xxxxxxx'
```
If you have already pushed the message, you can amend the commit and force push, but this is not recommended.
### I committed with the wrong name and email configured
If it's a single commit, amend it
```sh
$ git commit --amend --no-edit --author "New Authorname "
```
An alternative is to correctly configure your author settings in `git config --global author.(name|email)` and then use
```sh
$ git commit --amend --reset-author --no-edit
```
If you need to change for multiple commits, you can use
```sh
$ git -c rebase.instructionFormat='%s%nexec GIT_COMMITTER_DATE="%cD" GIT_AUTHOR_DATE="%aD" git commit --amend --no-edit --reset-author' rebase -r
```
`` is a commit before all your bad commits. If you need to change all of history in the current branch including the root of the branch, put `--root` there instead.
If you need to change all of history, see the man page for `git filter-branch`.
Note this will change the history and a force push is required.
### I want to remove a file from the previous commit
In order to remove changes for a file from the previous commit, do the following:
```sh
$ git checkout HEAD^ myfile
$ git add myfile
$ git commit --amend --no-edit
```
In case the file was newly added to the commit and you want to remove it (from Git alone), do:
```sh
$ git rm --cached myfile
$ git commit --amend --no-edit
```
This is particularly useful when you have an open patch and you have committed an unnecessary file, and need to force push to update the patch on a remote. The `--no-edit` option is used to keep the existing commit message.
### I want to move a change from one commit to another
If you've made a commit that includes changes that would fit better in another commit, you can move the changes to the other commit using an interactive rebase. This comes from [stackoverflow](https://stackoverflow.com/a/54985304/2491502).
For example, you have three commits (a, b, c). On b, you've changes file1 and file2 and you want to move the change on file1 from commit b to commit a.
First, rebase interactively:
```sh
$ git rebase -i HEAD~3
```
This will open an editor with the following:
```sh
pick a
pick b
pick c
```
Change the lines with a and b to edit:
```sh
edit a
edit b
pick c
```
Save and close the editor. This will bring you to commit b. Now, reset the file1 changes:
```sh
$ git reset HEAD~1 file1
```
This will unstage the changes in file1. Now, stash those changes and continue the rebase:
```sh
$ git stash
$ git rebase --continue
```
Now you will be editing commit a. Unstash the changes then add them to the current commit and continue the rebase:
```sh
$ git stash pop
$ git add file1
$ git commit --amend --no-edit
$ git rebase --continue
```
Now your rebase is complete, with the changes from b on a. If you wanted to move the changes from b to c, you would have to do two rebases since c comes before b: one to get the changes out of b, then another to edit c and add the stashed changes.
### I want to delete or remove my last commit
If you need to delete pushed commits, you can use the following. However, it will irreversibly change your history, and mess up the history of anyone else who had already pulled from the repository. In short, if you're not sure, you should never do this, ever.
```sh
$ git reset HEAD^ --hard
$ git push --force-with-lease [remote] [branch]
```
If you haven't pushed, to reset Git to the state it was in before you made your last commit (while keeping your staged changes):
```
(my-branch)$ git reset --soft HEAD^
```
This only works if you haven't pushed. If you have pushed, the only truly safe thing to do is `git revert SHAofBadCommit`. That will create a new commit that undoes all the previous commit's changes. Or, if the branch you pushed to is rebase-safe (ie. other devs aren't expected to pull from it), you can just use `git push --force-with-lease`. For more, see [the above section](#deleteremove-last-pushed-commit).
### Delete/remove arbitrary commit
The same warning applies as above. Never do this if possible.
```sh
$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push --force-with-lease [remote] [branch]
```
Or do an [interactive rebase](#interactive-rebase) and remove the line(s) corresponding to commit(s) you want to see removed.
### I tried to push my amended commit to a remote, but I got an error message
```sh
To https://github.com/yourusername/repo.git
! [rejected] mybranch -> mybranch (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```
Note that, as with rebasing (see below), amending **replaces the old commit with a new one**, so you must force push (`--force-with-lease`) your changes if you have already pushed the pre-amended commit to your remote. Be careful when you do this – *always* make sure you specify a branch!
```sh
(my-branch)$ git push origin mybranch --force-with-lease
```
In general, **avoid force pushing**. It is best to create and push a new commit rather than force-pushing the amended commit as it will cause conflicts in the source history for any other developer who has interacted with the branch in question or any child branches. `--force-with-lease` will still fail, if someone else was also working on the same branch as you, and your push would overwrite those changes.
If you are *absolutely* sure that nobody is working on the same branch or you want to update the tip of the branch *unconditionally*, you can use `--force` (`-f`), but this should be avoided in general.
### I accidentally did a hard reset, and I want my changes back
If you accidentally do `git reset --hard`, you can normally still get your commit back, as git keeps a log of everything for a few days.
Note: This is only valid if your work is backed up, i.e., either committed or stashed. `git reset --hard` _will remove_ uncommitted modifications, so use it with caution. (A safer option is `git reset --keep`.)
```sh
(main)$ git reflog
```
You'll see a list of your past commits, and a commit for the reset. Choose the SHA of the commit you want to return to, and reset again:
```sh
(main)$ git reset --hard SHA1234
```
And you should be good to go.
### I accidentally committed and pushed a merge
If you accidentally merged a feature branch to the main development branch before it was ready to be merged, you can still undo the merge. But there's a catch: A merge commit has more than one parent (usually two).
The command to use
```sh
(feature-branch)$ git revert -m 1
```
where the -m 1 option says to select parent number 1 (the branch into which the merge was made) as the parent to revert to.
Note: the parent number is not a commit identifier. Rather, a merge commit has a line `Merge: 8e2ce2d 86ac2e7`. The parent number is the 1-based index of the desired parent on this line, the first identifier is number 1, the second is number 2, and so on.
### I accidentally committed and pushed files containing sensitive data
If you accidentally pushed files containing sensitive, or private data (passwords, keys, etc.), you can amend the previous commit. Keep in mind that once you have pushed a commit, you should consider any data it contains to be compromised. These steps can remove the sensitive data from your public repo or your local copy, but you **cannot** remove the sensitive data from other people's pulled copies. If you committed a password, **change it immediately**. If you committed a key, **re-generate it immediately**. Amending the pushed commit is not enough, since anyone could have pulled the original commit containing your sensitive data in the meantime.
If you edit the file and remove the sensitive data, then run
```sh
(feature-branch)$ git add edited_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
If you want to remove an entire file (but keep it locally), then run
```sh
(feature-branch)$ git rm --cached sensitive_file
echo sensitive_file >> .gitignore
(feature-branch)$ git add .gitignore
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
Alternatively store your sensitive data in local environment variables.
If you want to completely remove an entire file (and not keep it locally), then run
```sh
(feature-branch)$ git rm sensitive_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
If you have made other commits in the meantime (i.e. the sensitive data is in a commit before the previous commit), you will have to rebase.
### I want to remove a large file from ever existing in repo history
If the file you want to delete is secret or sensitive, instead see [how to remove sensitive files](#undo-sensitive-commit-push).
Even if you delete a large or unwanted file in a recent commit, it still exists in git history, in your repo's `.git` folder, and will make `git clone` download unneeded files.
The actions in this part of the guide will require a force push, and rewrite large sections of repo history, so if you are working with remote collaborators, check first that any local work of theirs is pushed.
There are two options for rewriting history, the built-in `git-filter-branch` or [`bfg-repo-cleaner`](https://rtyley.github.io/bfg-repo-cleaner/). `bfg` is significantly cleaner and more performant, but it is a third-party download and requires java. We will describe both alternatives. The final step is to force push your changes, which requires special consideration on top of a regular force push, given that a great deal of repo history will have been permanently changed.
#### Recommended Technique: Use third-party bfg
Using bfg-repo-cleaner requires java. Download the bfg jar from the link [here](https://rtyley.github.io/bfg-repo-cleaner/). Our examples will use `bfg.jar`, but your download may have a version number, e.g. `bfg-1.13.0.jar`.
To delete a specific file.
```sh
(main)$ git rm path/to/filetoremove
(main)$ git commit -m "Commit removing filetoremove"
(main)$ java -jar ~/Downloads/bfg.jar --delete-files filetoremove
```
Note that in bfg you must use the plain file name even if it is in a subdirectory.
You can also delete a file by pattern, e.g.:
```sh
(main)$ git rm *.jpg
(main)$ git commit -m "Commit removing *.jpg"
(main)$ java -jar ~/Downloads/bfg.jar --delete-files *.jpg
```
With bfg, the files that exist on your latest commit will not be affected. For example, if you had several large .tga files in your repo, and then in an earlier commit, you deleted a subset of them, this call does not touch files present in the latest commit
Note, if you renamed a file as part of a commit, e.g. if it started as `LargeFileFirstName.mp4` and a commit changed it to `LargeFileSecondName.mp4`, running `java -jar ~/Downloads/bfg.jar --delete-files LargeFileSecondName.mp4` will not remove it from git history. Either run the `--delete-files` command with both filenames, or with a matching pattern.
#### Built-in Technique: Use git-filter-branch
`git-filter-branch` is more cumbersome and has less features, but you may use it if you cannot install or run `bfg`.
In the below, replace `filepattern` may be a specific name or pattern, e.g. `*.jpg`. This will remove files matching the pattern from all history and branches.
```sh
(main)$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch filepattern' --prune-empty --tag-name-filter cat -- --all
```
Behind-the-scenes explanation:
`--tag-name-filter cat` is a cumbersome, but simplest, way to apply the original tags to the new commits, using the command cat.
`--prune-empty` removes any now-empty commits.
#### Final Step: Pushing your changed repo history
Once you have removed your desired files, test carefully that you haven't broken anything in your repo - if you have, it is easiest to re-clone your repo to start over.
To finish, optionally use git garbage collection to minimize your local .git folder size, and then force push.
```sh
(main)$ git reflog expire --expire=now --all && git gc --prune=now --aggressive
(main)$ git push origin --force --tags
```
Since you just rewrote the entire git repo history, the `git push` operation may be too large, and return the error `“The remote end hung up unexpectedly”`. If this happens, you can try increasing the git post buffer:
```sh
(main)$ git config http.postBuffer 524288000
(main)$ git push --force
```
If this does not work, you will need to manually push the repo history in chunks of commits. In the command below, try increasing `` until the push operation succeeds.
```sh
(main)$ git push -u origin HEAD~:refs/head/main --force
```
Once the push operation succeeds the first time, decrease `` gradually until a conventional `git push` succeeds.
### I need to change the content of a commit which is not my last
Consider you created some (e.g. three) commits and later realize you missed doing something that belongs contextually into the first of those commits. This bothers you, because if you'd create a new commit containing those changes, you'd have a clean code base, but your commits weren't atomic (i.e. changes that belonged to each other weren't in the same commit). In such a situation you may want to change the commit where these changes belong to, include them and have the following commits unaltered. In such a case, `git rebase` might save you.
Consider a situation where you want to change the third last commit you made.
```sh
(your-branch)$ git rebase -i HEAD~4
```
gets you into interactive rebase mode, which allows you to edit any of your last three commits. A text editor pops up, showing you something like
```sh
pick 9e1d264 The third last commit
pick 4b6e19a The second to last commit
pick f4037ec The last commit
```
which you change into
```sh
edit 9e1d264 The third last commit
pick 4b6e19a The second to last commit
pick f4037ec The last commit
```
This tells rebase that you want to edit your third last commit and keep the other two unaltered. Then you'll save (and close) the editor. Git will then start to rebase. It stops on the commit you want to alter, giving you the chance to edit that commit. Now you can apply the changes which you missed applying when you initially committed that commit. You do so by editing and staging them. Afterwards you'll run
```sh
(your-branch)$ git commit --amend
```
which tells Git to recreate the commit. Also, Git will ask you to write a new commit message, using the original commit message as a starting point. Having done that, the hard part is solved.
```sh
(your-branch)$ git rebase --continue
```
will do the rest of the work for you.
## Staging
### I want to stage all tracked files and leave untracked files
```sh
$ git add -u
```
#### To stage part of tracked files
```sh
# to stage files with ext .txt
$ git add -u *.txt
# to stage all files inside directory src
$ git add -u src/
```
### I need to add staged changes to the previous commit
```sh
(my-branch*)$ git commit --amend
```
If you already know you don't want to change the commit message, you can tell git to reuse the commit message:
```sh
(my-branch*)$ git commit --amend -C HEAD
```
### I want to stage part of a new file, but not the whole file
Normally, if you want to stage part of a file, you run this:
```sh
$ git add --patch filename.x
```
`-p` will work for short. This will open interactive mode. You would be able to use the `s` option to split the commit - however, if the file is new, you will not have this option. To add a new file, do this:
```sh
$ git add -N filename.x
```
Then, you will need to use the `e` option to manually choose which lines to add. Running `git diff --cached` or
`git diff --staged` will show you which lines you have staged compared to which are still saved locally.
### I want to add changes in one file to two different commits
`git add` will add the entire file to a commit. `git add -p` will allow to interactively select which changes you want to add.
### I staged too many edits, and I want to break them out into a separate commit
`git reset -p` will open a patch mode reset dialog. This is similar to `git add -p`, except that selecting "yes" will unstage the change, removing it from the upcoming commit.
### I want to stage my unstaged edits, and unstage my staged edits
In many cases, you should unstage all of your staged files and then pick the file you want and commit it. However, if you want to switch the staged and unstaged edits, you can create a temporary commit to store your staged files, stage your unstaged files and then stash them. Then, reset the temporary commit and pop your stash.
```sh
$ git commit -m "WIP"
$ git add . # This will also add untracked files.
$ git stash
$ git reset HEAD^
$ git stash pop --index 0
```
NOTE 1: The reason to use `pop` here is want to keep idempotent as much as possible.
NOTE 2: Your staged files will be marked as unstaged if you don't use the `--index` flag. ([This link](https://stackoverflow.com/questions/31595873/git-stash-with-staged-files-does-stash-convert-staged-files-to-unstaged?answertab=active#tab-top) explains why.)
### I want to unstage a specific staged file
Sometimes we have one or more files that accidentally ended up being staged, and these files have not been committed before. To unstage them:
```sh
$ git reset --
```
This results in unstaging the file and make it look like it's untracked.
## Discarding changes
### I want to discard my local uncommitted changes (staged and unstaged)
If you want to discard all your local staged and unstaged changes, you can do this:
```sh
(my-branch)$ git reset --hard
# or
(main)$ git checkout -f
```
This will unstage all files you might have staged with `git add`:
```sh
$ git reset
```
This will revert all local uncommitted changes (should be executed in repo root):
```sh
$ git checkout .
```
You can also revert uncommitted changes to a particular file or directory:
```sh
$ git checkout [some_dir|file.txt]
```
Yet another way to revert all uncommitted changes (longer to type, but works from any subdirectory):
```sh
$ git reset --hard HEAD
```
This will remove all local untracked files, so only files tracked by Git remain:
```sh
$ git clean -fd
```
`-x` will also remove all ignored files.
### I want to discard specific unstaged changes
When you want to get rid of some, but not all changes in your working copy.
Checkout undesired changes, keep good changes.
```sh
$ git checkout -p
# Answer y to all of the snippets you want to drop
```
Another strategy involves using `stash`. Stash all the good changes, reset working copy, and reapply good changes.
```sh
$ git stash -p
# Select all of the snippets you want to save
$ git reset --hard
$ git stash pop
```
Alternatively, stash your undesired changes, and then drop stash.
```sh
$ git stash -p
# Select all of the snippets you don't want to save
$ git stash drop
```
### I want to discard specific unstaged files
When you want to get rid of one specific file in your working copy.
```sh
$ git checkout myFile
```
Alternatively, to discard multiple files in your working copy, list them all.
```sh
$ git checkout myFirstFile mySecondFile
```
### I want to discard only my unstaged local changes
When you want to get rid of all of your unstaged local uncommitted changes
```sh
$ git checkout .
```
### I want to discard all of my untracked files
When you want to get rid of all of your untracked files
```sh
$ git clean -f
```
## Branches
### I want to list all branches
List local branches
```sh
$ git branch
```
List remote branches
```sh
$ git branch -r
```
List all branches (both local and remote)
```sh
$ git branch -a
```
### Create a branch from a commit
```sh
$ git checkout -b
```
### I pulled from/into the wrong branch
This is another chance to use `git reflog` to see where your HEAD pointed before the bad pull.
```sh
(main)$ git reflog
ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward
c5bc55a HEAD@{1}: checkout: checkout message goes here
```
Simply reset your branch back to the desired commit:
```sh
$ git reset --hard c5bc55a
```
Done.
### I want to discard local commits so my branch is the same as one on the server
Confirm that you haven't pushed your changes to the server.
`git status` should show how many commits you are ahead of origin:
```sh
(my-branch)$ git status
# On branch my-branch
# Your branch is ahead of 'origin/my-branch' by 2 commits.
# (use "git push" to publish your local commits)
#
```
One way of resetting branch `my-branch` to match `origin/my-branch` (to have the same as what is on the remote) is to do this:
```sh
(my-branch)$ git reset --hard origin/my-branch
```
### I want to move my unstaged edits to a new branch
```sh
$ git checkout -b my-branch
```
### I want to move my unstaged edits to a different, existing branch
```sh
$ git stash
$ git checkout my-branch
$ git stash pop
```
### I committed to main instead of a new branch
Create the new branch while remaining on main:
```sh
(main)$ git branch my-branch
```
Reset the branch main to the previous commit:
```sh
(main)$ git reset --hard HEAD^
```
`HEAD^` is short for `HEAD^1`. This stands for the first parent of `HEAD`, similarly `HEAD^2` stands for the second parent of the commit (merges can have 2 parents).
Note that `HEAD^2` is **not** the same as `HEAD~2` (see [this link](http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde) for more information).
Alternatively, if you don't want to use `HEAD^`, find out what the commit hash you want to set your main branch to (`git log` should do the trick). Then reset to that hash. `git push` will make sure that this change is reflected on your remote.
For example, if the hash of the commit that your main branch is supposed to be at is `a13b85e`:
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
Checkout the new branch to continue working:
```sh
(main)$ git checkout my-branch
```
### I want to keep the whole file from another ref-ish
Say you have a working spike (see note), with hundreds of changes. Everything is working. Now, you commit into another branch to save that work:
```sh
(solution)$ git add -A && git commit -m "Adding all changes from this spike into one big commit."
```
When you want to put it into a branch (maybe feature, maybe `develop`), you're interested in keeping whole files. You want to split your big commit into smaller ones.
Say you have:
* branch `solution`, with the solution to your spike. One ahead of `develop`.
* branch `develop`, where you want to add your changes.
You can solve it bringing the contents to your branch:
```sh
(develop)$ git checkout solution -- file1.txt
```
This will get the contents of that file in branch `solution` to your branch `develop`:
```sh
# On branch develop
# Your branch is up-to-date with 'origin/develop'.
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: file1.txt
```
Then, commit as usual.
Note: Spike solutions are made to analyze or solve the problem. These solutions are used for estimation and discarded once everyone gets clear visualization of the problem. ~ [Wikipedia](https://en.wikipedia.org/wiki/Extreme_programming_practices).
### I made several commits on a single branch that should be on different branches
Say you are on your main branch. Running `git log`, you see you have made two commits:
```sh
(main)$ git log
commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee
Date: Tue Jul 22 15:39:27 2014 -0400
Bug #21 - Added CSRF protection
commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee
Date: Tue Jul 22 15:39:12 2014 -0400
Bug #14 - Fixed spacing on title
commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose
Date: Tue Jul 21 01:12:48 2014 -0400
First commit
```
Let's take note of our commit hashes for each bug (`e3851e8` for #21, `5ea5173` for #14).
First, let's reset our main branch to the correct commit (`a13b85e`):
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
Now, we can create a fresh branch for our bug #21:
```sh
(main)$ git checkout -b 21
(21)$
```
Now, let's *cherry-pick* the commit for bug #21 on top of our branch. That means we will be applying that commit, and only that commit, directly on top of whatever our head is at.
```sh
(21)$ git cherry-pick e3851e8
```
At this point, there is a possibility there might be conflicts. See the [**There were conflicts**](#merge-conflict) section in the [interactive rebasing section above](#interactive-rebase) for how to resolve conflicts.
Now let's create a new branch for bug #14, also based on main
```sh
(21)$ git checkout main
(main)$ git checkout -b 14
(14)$
```
And finally, let's cherry-pick the commit for bug #14:
```sh
(14)$ git cherry-pick 5ea5173
```
### I want to delete local branches that were deleted upstream
Once you merge a pull request on GitHub, it gives you the option to delete the merged branch in your fork. If you aren't planning to keep working on the branch, it's cleaner to delete the local copies of the branch so you don't end up cluttering up your working checkout with a lot of stale branches.
```sh
$ git fetch -p upstream
```
where, `upstream` is the remote you want to fetch from.
### I accidentally deleted my branch
If you're regularly pushing to remote, you should be safe most of the time. But still sometimes you may end up deleting your branches. Let's say we create a branch and create a new file:
```sh
(main)$ git checkout -b my-branch
(my-branch)$ git branch
(my-branch)$ touch foo.txt
(my-branch)$ ls
README.md foo.txt
```
Let's add it and commit.
```sh
(my-branch)$ git add .
(my-branch)$ git commit -m 'foo.txt added'
(my-branch)$ foo.txt added
1 files changed, 1 insertions(+)
create mode 100644 foo.txt
(my-branch)$ git log
commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
Author: siemiatj
Date: Wed Jul 30 00:34:10 2014 +0200
foo.txt added
commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson
Date: Tue Jul 29 13:14:46 2014 -0400
Fixes #6: Force pushing after amending commits
```
Now we're switching back to main and 'accidentally' removing our branch.
```sh
(my-branch)$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
(main)$ git branch -D my-branch
Deleted branch my-branch (was 4e3cd85).
(main)$ echo oh noes, deleted my branch!
oh noes, deleted my branch!
```
At this point you should get familiar with 'reflog', an upgraded logger. It stores the history of all the action in the repo.
```
(main)$ git reflog
69204cd HEAD@{0}: checkout: moving from my-branch to main
4e3cd85 HEAD@{1}: commit: foo.txt added
69204cd HEAD@{2}: checkout: moving from main to my-branch
```
As you can see we have commit hash from our deleted branch. Let's see if we can restore our deleted branch.
```sh
(main)$ git checkout -b my-branch-help
Switched to a new branch 'my-branch-help'
(my-branch-help)$ git reset --hard 4e3cd85
HEAD is now at 4e3cd85 foo.txt added
(my-branch-help)$ ls
README.md foo.txt
```
Voila! We got our removed file back. `git reflog` is also useful when rebasing goes terribly wrong.
### I want to delete a branch
To delete a remote branch:
```sh
(main)$ git push origin --delete my-branch
```
You can also do:
```sh
(main)$ git push origin :my-branch
```
To delete a local branch:
```sh
(main)$ git branch -d my-branch
```
To delete a local branch that *has not* been merged to the current branch or an upstream:
```sh
(main)$ git branch -D my-branch
```
### I want to delete multiple branches
Say you want to delete all branches that start with `fix/`:
```sh
(main)$ git branch | grep 'fix/' | xargs git branch -d
```
### I want to rename a branch
To rename the current (local) branch:
```sh
(main)$ git branch -m new-name
```
To rename a different (local) branch:
```sh
(main)$ git branch -m old-name new-name
```
To delete the `old-name` remote branch and push the `new-name` local branch:
```sh
(main)$ git push origin :old_name new_name
```
### I want to checkout to a remote branch that someone else is working on
First, fetch all branches from remote:
```sh
(main)$ git fetch --all
```
Say you want to checkout to `daves` from the remote.
```sh
(main)$ git checkout --track origin/daves
Branch daves set up to track remote branch daves from origin.
Switched to a new branch 'daves'
```
(`--track` is shorthand for `git checkout -b [branch] [remotename]/[branch]`)
This will give you a local copy of the branch `daves`, and any update that has been pushed will also show up remotely.
### I want to create a new remote branch from current local one
```sh
$ git push HEAD
```
If you would also like to set that remote branch as upstream for the current one, use the following instead:
```sh
$ git push -u HEAD
```
With the `upstream` mode and the `simple` (default in Git 2.0) mode of the `push.default` config, the following command will push the current branch with regards to the remote branch that has been registered previously with `-u`:
```sh
$ git push
```
The behavior of the other modes of `git push` is described in the [doc of `push.default`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-pushdefault).
### I want to set a remote branch as the upstream for a local branch
You can set a remote branch as the upstream for the current local branch using:
```sh
$ git branch --set-upstream-to [remotename]/[branch]
# or, using the shorthand:
$ git branch -u [remotename]/[branch]
```
To set the upstream remote branch for another local branch:
```sh
$ git branch -u [remotename]/[branch] [local-branch]
```
### I want to set my HEAD to track the default remote branch
By checking your remote branches, you can see which remote branch your HEAD is tracking. In some cases, this is not the desired branch.
```sh
$ git branch -r
origin/HEAD -> origin/gh-pages
origin/main
```
To change `origin/HEAD` to track `origin/main`, you can run this command:
```sh
$ git remote set-head origin --auto
origin/HEAD set to main
```
### I made changes on the wrong branch
You've made uncommitted changes and realise you're on the wrong branch. Stash changes and apply them to the branch you want:
```sh
(wrong_branch)$ git stash
(wrong_branch)$ git checkout
(correct_branch)$ git stash apply
```
### I want to split a branch into two
You've made a lot of commits on a branch and now want to separate it into two, ending with a branch up to an earlier commit and another with all the changes.
Use `git log` to find the commit where you want to split. Then do the following:
```sh
(original_branch)$ git checkout -b new_branch
(new_branch)$ git checkout original_branch
(original_branch)$ git reset --hard
```
If you had previously pushed the `original_branch` to remote, you will need to do a force push. For more information check [Stack Overflow](https://stackoverflow.com/questions/28983458/how-to-split-a-branch-in-two-with-git/28983843#28983843)
## Rebasing and Merging
### I want to undo rebase/merge
You may have merged or rebased your current branch with a wrong branch, or you can't figure it out or finish the rebase/merge process. Git saves the original HEAD pointer in a variable called ORIG_HEAD before doing dangerous operations, so it is simple to recover your branch at the state before the rebase/merge.
```sh
(my-branch)$ git reset --hard ORIG_HEAD
```
### I rebased, but I don't want to force push
Unfortunately, you have to force push, if you want those changes to be reflected on the remote branch. This is because you have changed the history. The remote branch won't accept changes unless you force push. This is one of the main reasons many people use a merge workflow, instead of a rebasing workflow - large teams can get into trouble with developers force pushing. Use this with caution. A safer way to use rebase is not to reflect your changes on the remote branch at all, and instead to do the following:
```sh
(main)$ git checkout my-branch
(my-branch)$ git rebase -i main
(my-branch)$ git checkout main
(main)$ git merge --ff-only my-branch
```
For more, see [this SO thread](https://stackoverflow.com/questions/11058312/how-can-i-use-git-rebase-without-requiring-a-forced-push).
### I need to combine commits
Let's suppose you are working in a branch that is/will become a pull-request against `main`. In the simplest case when all you want to do is to combine *all* commits into a single one and you don't care about commit timestamps, you can reset and recommit. Make sure the main branch is up to date and all your changes committed, then:
```sh
(my-branch)$ git reset --soft main
(my-branch)$ git commit -am "New awesome feature"
```
If you want more control, and also to preserve timestamps, you need to do something called an interactive rebase:
```sh
(my-branch)$ git rebase -i main
```
If you aren't working against another branch you'll have to rebase relative to your `HEAD`. If you want to squash the last 2 commits, for example, you'll have to rebase against `HEAD~2`. For the last 3, `HEAD~3`, etc.
```sh
(main)$ git rebase -i HEAD~2
```
After you run the interactive rebase command, you will see something like this in your text editor:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
pick b729ad5 fixup
pick e3851e8 another fix
# Rebase 8074d12..b729ad5 onto 8074d12
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```
All the lines beginning with a `#` are comments, they won't affect your rebase.
Then you replace `pick` commands with any in the list above, and you can also remove commits by removing corresponding lines.
For example, if you want to **leave the oldest (first) commit alone and combine all the following commits with the second oldest**, you should edit the letter next to each commit except the first and the second to say `f`:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
f b729ad5 fixup
f e3851e8 another fix
```
If you want to combine these commits **and rename the commit**, you should additionally add an `r` next to the second commit or simply use `s` instead of `f`:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
s b729ad5 fixup
s e3851e8 another fix
```
You can then rename the commit in the next text prompt that pops up.
```vim
Newer, awesomer features
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 8074d12
# You are currently editing a commit while rebasing branch 'main' on '8074d12'.
#
# Changes to be committed:
# modified: README.md
#
```
If everything is successful, you should see something like this:
```sh
(main)$ Successfully rebased and updated refs/heads/main.
```
#### Safe merging strategy
`--no-commit` performs the merge but pretends the merge failed and does not autocommit, giving the user a chance to inspect and further tweak the merge result before committing. `no-ff` maintains evidence that a feature branch once existed, keeping project history consistent.
```sh
(main)$ git merge --no-ff --no-commit my-branch
```
#### I need to merge a branch into a single commit
```sh
(main)$ git merge --squash my-branch
```
#### I want to combine only unpushed commits
Sometimes you have several work in progress commits that you want to combine before you push them upstream. You don't want to accidentally combine any commits that have already been pushed upstream because someone else may have already made commits that reference them.
```sh
(main)$ git rebase -i @{u}
```
This will do an interactive rebase that lists only the commits that you haven't already pushed, so it will be safe to reorder/fix/squash anything in the list.
#### I need to abort the merge
Sometimes the merge can produce problems in certain files, in those cases we can use the option `abort` to abort the current conflict resolution process, and try to reconstruct the pre-merge state.
```sh
(my-branch)$ git merge --abort
```
This command is available since Git version >= 1.7.4
### I need to update the parent commit of my branch
Say I have a main branch, a feature-1 branch branched from main, and a feature-2 branch branched off of feature-1. If I make a commit to feature-1, then the parent commit of feature-2 is no longer accurate (it should be the head of feature-1, since we branched off of it). We can fix this with `git rebase --onto`.
```sh
(feature-2)$ git rebase --onto feature-1 feature-2
```
This helps in sticky scenarios where you might have a feature built on another feature that hasn't been merged yet, and a bugfix on the feature-1 branch needs to be reflected in your feature-2 branch.
### Check if all commits on a branch are merged
To check if all commits on a branch are merged into another branch, you should diff between the heads (or any commits) of those branches:
```sh
(main)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
```
This will tell you if any commits are in one but not the other, and will give you a list of any nonshared between the branches. Another option is to do this:
```sh
(main)$ git log main ^feature/120-on-scroll --no-merges
```
### Possible issues with interactive rebases
#### The rebase editing screen says 'noop'
If you're seeing this:
```
noop
```
That means you are trying to rebase against a branch that is at an identical commit, or is *ahead* of your current branch. You can try:
* making sure your main branch is where it should be
* rebase against `HEAD~2` or earlier instead
#### There were conflicts
If you are unable to successfully complete the rebase, you may have to resolve conflicts.
First run `git status` to see which files have conflicts in them:
```sh
(my-branch)$ git status
On branch my-branch
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
both modified: README.md
```
In this example, `README.md` has conflicts. Open that file and look for the following:
```vim
<<<<<<< HEAD
some code
=========
some code
>>>>>>> new-commit
```
You will need to resolve the differences between the code that was added in your new commit (in the example, everything from the middle line to `new-commit`) and your `HEAD`.
If you want to keep one branch's version of the code, you can use `--ours` or `--theirs`:
```sh
(main*)$ git checkout --ours README.md
```
- When *merging*, use `--ours` to keep changes from the local branch, or `--theirs` to keep changes from the other branch.
- When *rebasing*, use `--theirs` to keep changes from the local branch, or `--ours` to keep changes from the other branch. For an explanation of this swap, see [this note in the Git documentation](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---merge).
If the merges are more complicated, you can use a visual diff editor:
```sh
(main*)$ git mergetool -t opendiff
```
After you have resolved all conflicts and tested your code, `git add` the files you have changed, and then continue the rebase with `git rebase --continue`
```sh
(my-branch)$ git add README.md
(my-branch)$ git rebase --continue
```
If after resolving all the conflicts you end up with an identical tree to what it was before the commit, you need to `git rebase --skip` instead.
If at any time you want to stop the entire rebase and go back to the original state of your branch, you can do so:
```sh
(my-branch)$ git rebase --abort
```
## Stash
### Stash all edits
To stash all the edits in your working directory
```sh
$ git stash
```
If you also want to stash untracked files, use `-u` option.
```sh
$ git stash -u
```
### Stash specific files
To stash only one file from your working directory
```sh
$ git stash push working-directory-path/filename.ext
```
To stash multiple files from your working directory
```sh
$ git stash push working-directory-path/filename1.ext working-directory-path/filename2.ext
```
### Stash with message
```sh
$ git stash save
```
or
```sh
$ git stash push -m
```
### Apply a specific stash from list
First check your list of stashes with message using
```sh
$ git stash list
```
Then apply a specific stash from the list using
```sh
$ git stash apply "stash@{n}"
```
Here, 'n' indicates the position of the stash in the stack. The topmost stash will be position 0.
Furthermore, using a time-based stash reference is also possible.
```sh
$ git stash apply "stash@{2.hours.ago}"
```
### Stash while keeping unstaged edits
You can manually create a `stash commit`, and then use `git stash store`.
```sh
$ git stash create
$ git stash store -m CREATED_SHA1
```
## Finding
### I want to find a string in any commit
To find a certain string which was introduced in any commit, you can use the following structure:
```sh
$ git log -S "string to find"
```
Commons parameters:
* `--source` means to show the ref name given on the command line by which each commit was reached.
* `--all` means to start from every branch.
* `--reverse` prints in reverse order, it means that will show the first commit that made the change.
### I want to find by author/committer
To find all commits by author/committer you can use:
```sh
$ git log --author=
$ git log --committer=
```
Keep in mind that author and committer are not the same. The `--author` is the person who originally wrote the code; on the other hand, the `--committer`, is the person who committed the code on behalf of the original author.
### I want to list commits containing specific files
To find all commits containing a specific file you can use:
```sh
$ git log --
```
You would usually specify an exact path, but you may also use wild cards in the path and file name:
```sh
$ git log -- **/*.js
```
While using wildcards, it's useful to inform `--name-status` to see the list of committed files:
```sh
$ git log --name-status -- **/*.js
```
### I want to view the commit history for a specific function
To trace the evolution of a single function you can use:
```sh
$ git log -L :FunctionName:FilePath
```
Note that you can combine this with further `git log` options, like [revision ranges](https://git-scm.com/docs/gitrevisions) and [commit limits](https://git-scm.com/docs/git-log/#_commit_limiting).
### Find a tag where a commit is referenced
To find all tags containing a specific commit:
```sh
$ git tag --contains
```
## Submodules
### Clone all submodules
```sh
$ git clone --recursive git://github.com/foo/bar.git
```
If already cloned:
```sh
$ git submodule update --init --recursive
```
### Remove a submodule
Creating a submodule is pretty straight-forward, but deleting them less so. The commands you need are:
```sh
$ git submodule deinit submodulename
$ git rm submodulename
$ git rm --cached submodulename
$ rm -rf .git/modules/submodulename
```
## Miscellaneous Objects
### Copy a folder or file from one branch to another
```sh
$ git checkout --
```
### Restore a deleted file
First find the commit when the file last existed:
```sh
$ git rev-list -n 1 HEAD -- filename
```
Then checkout that file:
```
git checkout deletingcommitid^ -- filename
```
### Delete tag
```sh
$ git tag -d
$ git push :refs/tags/
```
### Recover a deleted tag
If you want to recover a tag that was already deleted, you can do so by following these steps: First, you need to find the unreachable tag:
```sh
$ git fsck --unreachable | grep tag
```
Make a note of the tag's hash. Then, restore the deleted tag with following, making use of [`git update-ref`](https://git-scm.com/docs/git-update-ref):
```sh
$ git update-ref refs/tags/
```
Your tag should now have been restored.
### Deleted Patch
If someone has sent you a pull request on GitHub, but then deleted their original fork, you will be unable to clone their repository or to use `git am` as the [.diff, .patch](https://github.com/blog/967-github-secrets) URLs become unavailable. But you can checkout the PR itself using [GitHub's special refs](https://gist.github.com/piscisaureus/3342247). To fetch the content of PR#1 into a new branch called pr_1:
```sh
$ git fetch origin refs/pull/1/head:pr_1
From github.com:foo/bar
* [new ref] refs/pull/1/head -> pr_1
```
### Exporting a repository as a Zip file
```sh
$ git archive --format zip --output /full/path/to/zipfile.zip main
```
### Push a branch and a tag that have the same name
If there is a tag on a remote repository that has the same name as a branch you will get the following error when trying to push that branch with a standard `$ git push ` command.
```sh
$ git push origin
error: dst refspec same matches more than one.
error: failed to push some refs to ''
```
Fix this by specifying you want to push the head reference.
```sh
$ git push origin refs/heads/
```
If you want to push a tag to a remote repository that has the same name as a branch, you can use a similar command.
```sh
$ git push origin refs/tags/
```
## Tracking Files
### I want to change a file name's capitalization, without changing the contents of the file
```sh
(main)$ git mv --force myfile MyFile
```
### I want to overwrite local files when doing a git pull
```sh
(main)$ git fetch --all
(main)$ git reset --hard origin/main
```
### I want to remove a file from Git but keep the file
```sh
(main)$ git rm --cached log.txt
```
### I want to revert a file to a specific revision
Assuming the hash of the commit you want is c5f567:
```sh
(main)$ git checkout c5f567 -- file1/to/restore file2/to/restore
```
If you want to revert to changes made just 1 commit before c5f567, pass the commit hash as c5f567~1:
```sh
(main)$ git checkout c5f567~1 -- file1/to/restore file2/to/restore
```
### I want to list changes of a specific file between commits or branches
Assuming you want to compare last commit with file from commit c5f567:
```sh
$ git diff HEAD:path_to_file/file c5f567:path_to_file/file
# or
$ git diff HEAD c5f567 -- path_to_file/file
```
If you are going to compare changes between the tips of the `main` and the `staging` branches:
```sh
$ git diff main:path_to_file/file staging:path_to_file/file
# or
$ git diff main staging -- path_to_file/file
```
### I want Git to ignore changes to a specific file
This works great for config templates or other files that require locally adding credentials that shouldn't be committed.
```sh
$ git update-index --assume-unchanged file-to-ignore
```
Note that this does *not* remove the file from source control - it is only ignored locally. To undo this and tell Git to notice changes again, this clears the ignore flag:
```sh
$ git update-index --no-assume-unchanged file-to-stop-ignoring
```
## Debugging with Git
The [git-bisect](https://git-scm.com/docs/git-bisect) command uses a binary search to find which commit in your Git history introduced a bug.
Suppose you're on the `main` branch, and you want to find the commit that broke some feature. You start bisect:
```sh
$ git bisect start
```
Then you should specify which commit is bad, and which one is known to be good. Assuming that your *current* version is bad, and `v1.1.1` is good:
```sh
$ git bisect bad
$ git bisect good v1.1.1
```
Now `git-bisect` selects a commit in the middle of the range that you specified, checks it out, and asks you whether it's good or bad. You should see something like:
```sh
$ Bisecting: 5 revision left to test after this (roughly 5 step)
$ [c44abbbee29cb93d8499283101fe7c8d9d97f0fe] Commit message
$ (c44abbb)$
```
You will now check if this commit is good or bad. If it's good:
```sh
$ (c44abbb)$ git bisect good
```
and `git-bisect` will select another commit from the range for you. This process (selecting `good` or `bad`) will repeat until there are no more revisions left to inspect, and the command will finally print a description of the **first** bad commit.
## Configuration
### I want to add aliases for some Git commands
On OS X and Linux, your git configuration file is stored in ```~/.gitconfig```. I've added some example aliases I use as shortcuts (and some of my common typos) in the ```[alias]``` section as shown below:
```vim
[alias]
a = add
amend = commit --amend
c = commit
ca = commit --amend
ci = commit -a
co = checkout
d = diff
dc = diff --changed
ds = diff --staged
extend = commit --amend -C HEAD
f = fetch
loll = log --graph --decorate --pretty=oneline --abbrev-commit
m = merge
one = log --pretty=oneline
outstanding = rebase -i @{u}
reword = commit --amend --only
s = status
unpushed = log @{u}
wc = whatchanged
wip = rebase -i @{u}
zap = fetch -p
day = log --reverse --no-merges --branches=* --date=local --since=midnight --author=\"$(git config --get user.name)\"
delete-merged-branches = "!f() { git checkout --quiet main && git branch --merged | grep --invert-match '\\*' | xargs -n 1 git branch --delete; git checkout --quiet @{-1}; }; f"
```
### I want to add an empty directory to my repository
You can’t! Git doesn’t support this, but there’s a hack. You can create a .gitignore file in the directory with the following contents:
```
# Ignore everything in this directory
*
# Except this file
!.gitignore
```
Another common convention is to make an empty file in the folder, titled .gitkeep.
```sh
$ mkdir mydir
$ touch mydir/.gitkeep
```
You can also name the file as just .keep , in which case the second line above would be ```touch mydir/.keep```
### I want to cache a username and password for a repository
You might have a repository that requires authentication. In which case you can cache a username and password so you don't have to enter it on every push and pull. Credential helper can do this for you.
```sh
$ git config --global credential.helper cache
# Set git to use the credential memory cache
```
```sh
$ git config --global credential.helper 'cache --timeout=3600'
# Set the cache to timeout after 1 hour (setting is in seconds)
```
To find a credential helper:
```sh
$ git help -a | grep credential
# Shows you possible credential helpers
```
For OS specific credential caching:
```sh
$ git config --global credential.helper osxkeychain
# For OSX
```
```sh
$ git config --global credential.helper manager
# Git for Windows 2.7.3+
```
```sh
$ git config --global credential.helper gnome-keyring
# Ubuntu and other GNOME-based distros
```
More credential helpers can likely be found for different distributions and operating systems.
### I want to make Git ignore permissions and filemode changes
```sh
$ git config core.fileMode false
```
If you want to make this the default behaviour for logged-in users, then use:
```sh
$ git config --global core.fileMode false
```
### I want to set a global user
To configure user information used across all local repositories, and to set a name that is identifiable for credit when review version history:
```sh
$ git config --global user.name “[firstname lastname]”
```
To set an email address that will be associated with each history marker:
```sh
git config --global user.email “[valid-email]”
```
## I've no idea what I did wrong
So, you're screwed - you `reset` something, or you merged the wrong branch, or you force pushed and now you can't find your commits. You know, at some point, you were doing alright, and you want to go back to some state you were at.
This is what `git reflog` is for. `reflog` keeps track of any changes to the tip of a branch, even if that tip isn't referenced by a branch or a tag. Basically, every time HEAD changes, a new entry is added to the reflog. This only works for local repositories, sadly, and it only tracks movements (not changes to a file that weren't recorded anywhere, for instance).
```sh
(main)$ git reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
```
The reflog above shows a checkout from main to the 2.2 branch and back. From there, there's a hard reset to an older commit. The latest activity is represented at the top labeled `HEAD@{0}`.
If it turns out that you accidentally moved back, the reflog will contain the commit main pointed to (0254ea7) before you accidentally dropped 2 commits.
```sh
$ git reset --hard 0254ea7
```
Using `git reset` it is then possible to change main back to the commit it was before. This provides a safety net in case history was accidentally changed.
(copied and edited from [Source](https://www.atlassian.com/git/tutorials/rewriting-history/git-reflog)).
## Git Shortcuts
### Git Bash
Once you're comfortable with what the above commands are doing, you might want to create some shortcuts for Git Bash. This allows you to work a lot faster by doing complex tasks in really short commands.
```sh
alias sq=squash
function squash() {
git rebase -i HEAD~$1
}
```
Copy those commands to your .bashrc or .bash_profile.
### PowerShell on Windows
If you are using PowerShell on Windows, you can also set up aliases and functions. Add these commands to your profile, whose path is defined in the `$profile` variable. Learn more at the [About Profiles](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles) page on the Microsoft documentation site.
```powershell
Set-Alias sq Squash-Commits
function Squash-Commits {
git rebase -i HEAD~$1
}
```
# Other Resources
## Books
* [Learn Enough Git to Be Dangerous](https://www.learnenough.com/git-tutorial) - A book by Michael Hartl covering Git from basics
* [Pro Git](https://git-scm.com/book/en/v2) - Scott Chacon and Ben Straub's excellent book about Git
* [Git Internals](https://github.com/pluralsight/git-internals-pdf) - Scott Chacon's other excellent book about Git
* [Nasa handbook](https://www.nasa.gov/wp-content/uploads/2018/09/nasa_systems_engineering_handbook_0.pdf)
## Tutorials
* [19 Git Tips For Everyday Use](https://www.alexkras.com/19-git-tips-for-everyday-use) - A list of useful Git one liners
* [Atlassian's Git tutorial](https://www.atlassian.com/git/tutorials) Get Git right with tutorials from beginner to advanced.
* [Getting solid at Git rebase vs. merge](https://medium.com/@porteneuve/getting-solid-at-git-rebase-vs-merge-4fa1a48c53aa)
* [Git and GitHub Cartoons](https://allisonhorst.com/git-github) – Cartoons by Allison Horst that showcase how to use Git.
* [Git Commands and Best Practices Cheat Sheet](https://zeroturnaround.com/rebellabs/git-commands-and-best-practices-cheat-sheet) - A Git cheat sheet in a blog post with more explanations
* [Git from the inside out](https://codewords.recurse.com/issues/two/git-from-the-inside-out) - A tutorial that dives into Git's internals
* [git-workflow](https://github.com/asmeurer/git-workflow) - [Aaron Meurer](https://github.com/asmeurer)'s howto on using Git to contribute to open source repositories
* [GitHub as a workflow](https://hugogiraudel.com/2015/08/13/github-as-a-workflow/) - An interesting take on using GitHub as a workflow, particularly with empty PRs
* [Githug](https://github.com/Gazler/githug) - A game to learn more common Git workflows
* [Learn Git branching](https://learngitbranching.js.org/) An interactive web based branching/merging/rebasing tutorial
* [learnGitBranching](https://github.com/pcottle/learnGitBranching) - An interactive git visualization to challenge and educate!
## Scripts and Tools
* [firstaidgit.io](http://firstaidgit.io/) A searchable selection of the most frequently asked Git questions
* [git-extra-commands](https://github.com/unixorn/git-extra-commands) - a collection of useful extra Git scripts
* [git-extras](https://github.com/tj/git-extras) - GIT utilities -- repo summary, repl, changelog population, author commit percentages and more
* [git-fire](https://github.com/qw3rtman/git-fire) - git-fire is a Git plugin that helps in the event of an emergency by adding all current files, committing, and pushing to a new branch (to prevent merge conflicts).
* [git-tips](https://github.com/git-tips/tips) - Small Git tips
* [git-town](https://github.com/Originate/git-town) - Generic, high-level Git workflow support! http://www.git-town.com
## GUI Clients
* [GitKraken](https://www.gitkraken.com/) - The downright luxurious Git client, for Windows, Mac & Linux
* [git-cola](https://git-cola.github.io/) - another Git client for Windows and OS X
* [GitUp](https://github.com/git-up/GitUp) - A newish GUI that has some very opinionated ways of dealing with Git's complications
* [gitx-dev](https://rowanj.github.io/gitx/) - another graphical Git client for OS X
* [Sourcetree](https://www.sourcetreeapp.com/) - Simplicity meets power in a beautiful and free Git GUI. For Windows and Mac.
* [Tower](https://www.git-tower.com/) - graphical Git client for OS X (paid)
* [tig](https://jonas.github.io/tig/) - terminal text-mode interface for Git
* [Magit](https://magit.vc/) - Interface to Git implemented as an Emacs package.
* [GitExtensions](https://github.com/gitextensions/gitextensions) - a shell extension, a Visual Studio 2010-2015 plugin and a standalone Git repository tool.
* [Fork](https://git-fork.com/) - a fast and friendly Git client for Mac (beta)
* [gmaster](https://gmaster.io/) - a Git client for Windows that has 3-way merge, analyze refactors, semantic diff and merge (beta)
* [gitk](https://git-scm.com/docs/gitk) - a Git client for linux to allow simple view of repo state.
* [SublimeMerge](https://www.sublimemerge.com/) - Blazing fast, extensible client that provides 3-way merges, powerful search and syntax highlighting, in active development.
================================================
FILE: README_es.md
================================================
# Reglas de vuelo para git
🌍
*[English](README.md) ∙ [Español](README_es.md) ∙ [Русский](README_ru.md) ∙ [繁體中文](README_zh-TW.md) ∙ [简体中文](README_zh-CN.md) ∙ [한국어](README_kr.md) ∙ [Tiếng Việt](README_vi.md) ∙ [Français](README_fr.md) ∙ [日本語](README_ja.md)*
#### ¿Qué son "reglas de vuelo"?
Una guía para astronautas (ahora, programadores usando git) acerca de qué hacer cuando las cosas van mal.
> *Las reglas de vuelo* son los conocimientos adquiridos con tanto esfuerzo en manuales que enumeran, paso a paso, qué hacer si ocurre X y por qué. Básicamente, son procedimientos operativos extremadamente detallados y específicos de cada escenario. [...]
> La NASA ha estado capturando nuestros errores, desastres y soluciones desde principios de la década de 1960, cuando los equipos de tierra de la era Mercurio comenzaron a recopilar "lecciones aprendidas" en un compendio que ahora enumera miles de situaciones problemáticas, desde fallas en el motor hasta fallas reventadas en computadoras, y sus soluciones.
— Chris Hadfield, *An Astronaut's Guide to Life*.
#### Convenciones para este documento
En aras de la claridad, todos los ejemplos de este documento usan un indicador de bash personalizado para señalar la rama actual y si hay cambios escalonados o no. La rama se incluye entre paréntesis, y un `*` al lado del nombre de la rama indica cambios realizados.
[](https://gitter.im/k88hudson/git-flight-rules?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Repositorios](#repositorios)
- [Quiero empezar un repositorio local](#quiero-empezar-un-repositorio-local)
- [Quiero clonar un repositorio remoto](#quiero-clonar-un-repositorio-remoto)
- [Editando commits](#editando-commits)
- [¿Qué acabo de hacer en el commit?](#%C2%BFqu%C3%A9-acabo-de-hacer-en-el-commit)
- [Escribí algo mal en el mensaje del commit](#escrib%C3%AD-algo-mal-en-el-mensaje-del-commit)
- [Hice un commit con el nombre y correo mal configurado](#hice-un-commit-con-el-nombre-y-correo-mal-configurado)
- [Quiero remover un archivo de un commit](#quiero-remover-un-archivo-de-un-commit)
- [Quiero borrar o remover mi último commit](#quiero-borrar-o-remover-mi-%C3%BAltimo-commit)
- [Eliminar/remover commit arbitrario](#eliminarremover-commit-arbitrario)
- [Intenté subir mi commit enmendado al repositorio remoto, pero obtuve un mensaje de error](#intent%C3%A9-subir-mi-commit-enmendado-al-repositorio-remoto-pero-obtuve-un-mensaje-de-error)
- [Accidentalmente hice un hard reset y quiero mis cambios de vuelta](#accidentalmente-hice-un-hard-reset-y-quiero-mis-cambios-de-vuelta)
- [Accidentalment hice un commit y empujé una fusión](#accidentalment-hice-un-commit-y-empuj%C3%A9-una-fusi%C3%B3n)
- [Accidentalmente hice un commit y empujé archivos que contienen data sensible](#accidentalmente-hice-un-commit-y-empuj%C3%A9-archivos-que-contienen-data-sensible)
- [Staging](#staging)
- [Necesito agregar otros cambios al commit anterior](#necesito-agregar-otros-cambios-al-commit-anterior)
- [Quiero agregar parte de un nuevo archivo, pero no todo el archivo](#quiero-agregar-parte-de-un-nuevo-archivo-pero-no-todo-el-archivo)
- [Quiero agregar cambios en un archivo a dos commits diferentes](#quiero-agregar-cambios-en-un-archivo-a-dos-commits-diferentes)
- [Quiero crear mis ediciones sin escalonar y eliminar mis ediciones escalonadas](#quiero-crear-mis-ediciones-sin-escalonar-y-eliminar-mis-ediciones-escalonadas)
- [Ediciones sin escena](#ediciones-sin-escena)
- [Deseo mover mis ediciones sin escena a una nueva rama](#deseo-mover-mis-ediciones-sin-escena-a-una-nueva-rama)
- [Deseo mover mis ediciones sin escena a una rama diferente existente](#deseo-mover-mis-ediciones-sin-escena-a-una-rama-diferente-existente)
- [Quiero descartar mis cambios locales no confirmados (en escena y sin escena)](#quiero-descartar-mis-cambios-locales-no-confirmados-en-escena-y-sin-escena)
- [Quiero descartar cambios específicos no planificados](#quiero-descartar-cambios-espec%C3%ADficos-no-planificados)
- [Quiero descartar archivos específicos no escaneados](#quiero-descartar-archivos-espec%C3%ADficos-no-escaneados)
- [Deseo descartar solo mis cambios locales sin escenario](#deseo-descartar-solo-mis-cambios-locales-sin-escenario)
- [Quiero descartar todos mis archivos sin seguimiento](#quiero-descartar-todos-mis-archivos-sin-seguimiento)
- [Ramas](#ramas)
- [Quiero enumerar todas las ramas](#quiero-enumerar-todas-las-ramas)
- [Crear una rama desde una confirmación](#crear-una-rama-desde-una-confirmaci%C3%B3n)
- [Hice pull de / en la rama incorrecta](#hice-pull-de--en-la-rama-incorrecta)
- [Quiero descartar confirmaciones locales para que mi rama sea la misma que la del servidor](#quiero-descartar-confirmaciones-locales-para-que-mi-rama-sea-la-misma-que-la-del-servidor)
- [Hice commit a main en lugar de una nueva rama](#hice-commit-a-main-en-lugar-de-una-nueva-rama)
- [Quiero mantener todo el archivo de otro ref-ish](#quiero-mantener-todo-el-archivo-de-otro-ref-ish)
- [Realicé varios commits en una sola rama que debería estar en diferentes ramas](#realic%C3%A9-varios-commits-en-una-sola-rama-que-deber%C3%ADa-estar-en-diferentes-ramas)
- [Quiero eliminar las ramas locales que se eliminaron en sentido ascendente](#quiero-eliminar-las-ramas-locales-que-se-eliminaron-en-sentido-ascendente)
- [Accidentalmente borré mi rama](#accidentalmente-borr%C3%A9-mi-rama)
- [Quiero eliminar una rama](#quiero-eliminar-una-rama)
- [Quiero eliminar varias ramas](#quiero-eliminar-varias-ramas)
- [Quiero cambiar el nombre de una rama](#quiero-cambiar-el-nombre-de-una-rama)
- [Quiero hacer checkout en una rama remota en la que alguien más está trabajando](#quiero-hacer-checkout-en-una-rama-remota-en-la-que-alguien-m%C3%A1s-est%C3%A1-trabajando)
- [Quiero crear una nueva rama remota desde la actual local](#quiero-crear-una-nueva-rama-remota-desde-la-actual-local)
- [Quiero configurar una rama remota como upstream para una rama local](#quiero-configurar-una-rama-remota-como-upstream-para-una-rama-local)
- [Quiero configurar mi HEAD para rastrear la rama remota predeterminada](#quiero-configurar-mi-head-para-rastrear-la-rama-remota-predeterminada)
- [Rebasing y Merging](#rebasing-y-merging)
- [Quiero deshacer rebase/merge](#quiero-deshacer-rebasemerge)
- [Hice rebase, pero no quiero forzar el push](#hice-rebase-pero-no-quiero-forzar-el-push)
- [Necesito combinar commits](#necesito-combinar-commits)
- [Estrategia de merge segura](#estrategia-de-merge-segura)
- [Necesito fusionar una rama en un solo commit](#necesito-fusionar-una-rama-en-un-solo-commit)
- [Quiero combinar solo los commits sin haber hecho push](#quiero-combinar-solo-los-commits-sin-haber-hecho-push)
- [Necesito abortar el merge](#necesito-abortar-el-merge)
- [Comprobar si se combinan todos los commits de un branch](#comprobar-si-se-combinan-todos-los-commits-de-un-branch)
- [Posibles problemas con rebase interactivos](#posibles-problemas-con-rebase-interactivos)
- [La pantalla de edición de rebase dice 'noop'](#la-pantalla-de-edici%C3%B3n-de-rebase-dice-noop)
- [Hubo conflictos](#hubo-conflictos)
- [Stash](#stash)
- [Usar stash en todos los cambios](#usar-stash-en-todos-los-cambios)
- [Usar stash para archivos específicos](#usar-stash-para-archivos-espec%C3%ADficos)
- [Usar stash con un mensaje](#usar-stash-con-un-mensaje)
- [Aplicar un stash específico de la lista](#aplicar-un-stash-espec%C3%ADfico-de-la-lista)
- [Búsqueda](#b%C3%BAsqueda)
- [Quiero encontra un string en algún commit](#quiero-encontra-un-string-en-alg%C3%BAn-commit)
- [Quiero buscar por autor / committer](#quiero-buscar-por-autor--committer)
- [Quiero enumerar commits que contienen archivos específicos](#quiero-enumerar-commits-que-contienen-archivos-espec%C3%ADficos)
- [Encontrar una etiqueta donde se hace referencia a un commit](#encontrar-una-etiqueta-donde-se-hace-referencia-a-un-commit)
- [Submódulos](#subm%C3%B3dulos)
- [Clonar todos los submódulos](#clonar-todos-los-subm%C3%B3dulos)
- [Remover un submódulo](#remover-un-subm%C3%B3dulo)
- [Objetos diversos](#objetos-diversos)
- [Restaurar un archivo eliminado](#restaurar-un-archivo-eliminado)
- [Eliminar una etiqueta](#eliminar-una-etiqueta)
- [Recuperar una etiqueta eliminada](#recuperar-una-etiqueta-eliminada)
- [Patch eliminado](#patch-eliminado)
- [Exportar un repositorio como un archivo Zip](#exportar-un-repositorio-como-un-archivo-zip)
- [Seguimiento de archivos](#seguimiento-de-archivos)
- [Quiero cambiar el uso de mayúsculas de un nombre de archivo, sin cambiar el contenido del archivo](#quiero-cambiar-el-uso-de-may%C3%BAsculas-de-un-nombre-de-archivo-sin-cambiar-el-contenido-del-archivo)
- [Quiero sobrescribir los archivos locales cuando hago un git pull](#quiero-sobrescribir-los-archivos-locales-cuando-hago-un-git-pull)
- [Quiero eliminar un archivo de Git pero mantener el archivo](#quiero-eliminar-un-archivo-de-git-pero-mantener-el-archivo)
- [Quiero revertir un archivo a una revisión específica](#quiero-revertir-un-archivo-a-una-revisi%C3%B3n-espec%C3%ADfica)
- [Configuración](#configuraci%C3%B3n)
- [Quiero agregar alias para algunos comandos de Git](#quiero-agregar-alias-para-algunos-comandos-de-git)
- [Quiero agregar un directorio vacío a mi repositorio](#quiero-agregar-un-directorio-vac%C3%ADo-a-mi-repositorio)
- [Quiero guardar en caché un nombre de usuario y contraseña para un repositorio](#quiero-guardar-en-cach%C3%A9-un-nombre-de-usuario-y-contrase%C3%B1a-para-un-repositorio)
- [Quiero hacer que Git ignore los permisos y cambios en el modo de archivo](#quiero-hacer-que-git-ignore-los-permisos-y-cambios-en-el-modo-de-archivo)
- [No tengo idea de lo que hice mal](#no-tengo-idea-de-lo-que-hice-mal)
- [Otros recursos](#otros-recursos)
- [Libros](#libros)
- [Tutoriales](#tutoriales)
- [Scripts y herramientas](#scripts-y-herramientas)
- [Clientes GUI](#clientes-gui)
## Repositorios
### Quiero empezar un repositorio local
Para inicializar un directorio existente como un repositorio de Git:
```sh
(my-folder) $ git init
```
### Quiero clonar un repositorio remoto
Para clonar (copiar) un repositorio remoto, copia la url del repositorio y ejecuta:
```sh
$ git clone [url]
```
## Editando commits
### ¿Qué acabo de hacer en el commit?
Digamos que simplemente hiciste cambios a ciegas con `git commit -a` y no estás seguro de cuál fue el contenido real de la confirmación que acabas de realizar. Puedes mostrar el último commit en su HEAD actual con:
```sh
(main)$ git show
```
o
```sh
$ git log -n1 -p
```
### Escribí algo mal en el mensaje del commit
Si escribiste algo mal y todavía no has subido tu commit, puedes hacer lo siguiente para cambiar el mensaje del commit:
```sh
$ git commit --amend --only
```
Esto abrirá tu editor de texto por defecto, donde puedes editar el mensaje. Por otro lado, tú puedes hacer todo esto con un solo comando:
```sh
$ git commit --amend --only -m 'xxxxxxx'
```
Si ya has subido tu commit, puedes corregirlo usando amend y luego forzar el push, pero esto no es recomendado.
### Hice un commit con el nombre y correo mal configurado
Si es un solo commit, corrígelo
```sh
$ git commit --amend --author "Nuevo autor "
```
Si necesitas cambiar todo el historial, mira la página 'git filter-branch' del manual
### Quiero remover un archivo de un commit
Para remover un archivo de un commit, haz lo siguiente:
```sh
$ git checkout HEAD^ miArchivo
$ git add -A
$ git commit --amend
```
Esto es particularmente útil cuando tienes un patch abierto y has hecho commit de un archivo innecesario, necesitar forzar el push para actualizar el parche en un control remoto.
### Quiero borrar o remover mi último commit
Si necesitas eliminar commits, puedes usar lo siguiente. Sin embargo, cambiará irreversiblemente su historial y arruinará la historia de cualquier otra persona que ya haya clonado el repositorio. En resumen, si no estás seguro, nunca deberías hacer esto, nunca.
```sh
$ git reset HEAD^ --hard
$ git push --force-with-lease [remote] [branch]
```
Si no has subido tus cambios, para resetear Git al estado en el que estaba antes de realizar tu último commit (mientras mantengas tus cambios en staging):
```
(my-branch)$ git reset --soft HEAD^
```
Esto solo funciona si no subiste tu commit. Si lo hiciste, la única cosa segura por hacer es `git revert SHAofBadCommit`. Eso creará un nuevo commit que deshace todos los cambios del anterior commit. O, si la rama que subiste es segura ante reorganizaciones (ej. otros desarrolladores no esperan recibir cambios desde ahí), puedes usar `git push --force-with-lease`. Para más, mira [la sección de arriba](#quiero-borrar-o-remover-mi-ultimo-commit).
### Eliminar/remover commit arbitrario
La misma advertencia de arriba. Nunca hagas esto si es posible.
```sh
$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push --force-with-lease [remote] [branch]
```
O haz un [rebase-interactivo](#rebase-interactivo) y remueve la(s) línea(s) correspondientes al commit que quieres remover.
### Intenté subir mi commit enmendado al repositorio remoto, pero obtuve un mensaje de error
```sh
To https://github.com/yourusername/repo.git
! [rejected] mybranch -> mybranch (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```
Ten en cuenta que, al igual que con el rebase (ver más abajo), usar amend **reemplaza el anterior commit con uno nuevo**, por lo que debes forzar el push (`--force-with-lease`) de tus cambios si ya has hecho una confirmación previamente enmendada al repositorio remoto. ¡Ten cuidado cuando hagas esto – *siempre* asegúrate de especificar una rama!
```sh
(my-branch)$ git push origin mybranch --force-with-lease
```
En general, **evita forzar el push**. Es mejor crear y subir un nuevo commit que forzar el commit enmendado porque causará conflictos en el historial fuente para cualquier otro desarrolador que ha interactuado con la rama en cuestión o una de sus ramas hijas. `--force-with-lease` aún fallará, si alguien más estuviera trabajando en la misma rama que tú, y tu push forzado sobrescribiría sus cambios.
Si estás *absolutamente* seguro que nadie está trabajando en la misma rama o que tú quieres actualizar la rama *incondicionalmente*, puedes usar `--force` (`-f`), pero esto debería ser evitado en general.
### Accidentalmente hice un hard reset y quiero mis cambios de vuelta
Si accidentalmente hiciste `git reset --hard`, puedes volver a obtener tus commits de vuelta ya que git mantiene un registro de todo durante unos días.
```sh
(main)$ git reflog
```
Verás una lista de tus antiguos commits, y un commit para el reset. Escoge el SHA del commit al que quieres retornar y has el reset de nuevo:
```sh
(main)$ git reset --hard SHA1234
```
Y deberías estar ubicado en ese commit.
### Accidentalment hice un commit y empujé una fusión
Si accidentalmente fusionaste una rama a la principal de desarrollo antes de que esté lista para fusionar, todavía puedes deshacer esa fusión. Pero hay un problema: Un commit de fusión tiene más de un padre (usualmente 2).
El comando a usar
```sh
(feature-branch)$ git revert -m 1
```
donde la opción -m 1 option menciona seleccionar el padre número 1 (la rama en la cual se hizo la fusión) como el padre a revertirlo.
Nota: el número padre no es un identificador de commit. Más bien, un commit de fusión tiene una línea `Merge: 8e2ce2d 86ac2e7`. El número padre empieza con el número 1 como índice, el primer identificador es número 1, el segundo es el número 2, y así
### Accidentalmente hice un commit y empujé archivos que contienen data sensible
Si accidentalment empujaste archivos que contienen data sensible (contraseñas, llaves, etc.), puedes modificar el commit previo. Ten en mente que una vez que hayas hecho un commit, debes considerar cualquier información que éste contiene para ser empujado. Estos pasos pueden remover la data sensible de tu repo público o tu copia local, pero **no puedes** remover la data sensible de copias jaladas de otras personas. Si quieres hacer un commit de una contraseña, **cámbialo de inmediato**. Si hiciste commit de una llave, **regenérala de inmediato**. Modificar el commit enviado no es suficiente, ya que cualquiera podría haber retirado el commit original que contiene sus datos confidenciales en ese tiempo.
Si editas un archivo y remueves la data sensible, entonces ejecuta
```sh
(feature-branch)$ git add edited_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
Si quieres remover un archivo entero (pero mantenerlo localmente), entonces ejecuta
```sh
(feature-branch)$ git rm --cached sensitive_file
echo sensitive_file >> .gitignore
(feature-branch)$ git add .gitignore
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
Alternativamente guarda tu data sensible en variables de entorno locales.
Si quieres remover completamente un archivo completo (y no mantenerlo localmente), entonces ejecuta
```sh
(feature-branch)$ git rm sensitive_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
Si haz hecho otros commits durante ese tiempo (ej. la data sensible está en un commit antes de ese commit), necesitarás hacer un rebase.
## Staging
### Necesito agregar otros cambios al commit anterior
```sh
(mi-rama*)$ git commit --amend
```
### Quiero agregar parte de un nuevo archivo, pero no todo el archivo
Normalmente, si deseas representar parte de un archivo, ejecuta esto:
```sh
$ git add --patch archivo.x
```
`-p` funcionará para abreviar. Esto abrirá el modo interactivo. Puedes usar la opción `s` para dividir la confirmación; sin embargo, si el archivo es nuevo, no tendrás esta opción. Para agregar un nuevo archivo, usa esto:
```sh
$ git add -N archivo.x
```
Luego, necesitarás usar la opción `e` para elegir manualmente qué líneas agregar. Ejecutando `git diff --cached` o
`git diff -staged` se mostrará qué líneas ha comparado con las que todavía se guardan localmente.
### Quiero agregar cambios en un archivo a dos commits diferentes
`git add` agregará el archivo completo a un commit. `git add -p` te permitirá seleccionar interactivamente los cambios que deseas agregar.
### Quiero crear mis ediciones sin escalonar y eliminar mis ediciones escalonadas
Esto es complicado. Lo mejor que creo es que debes esconder tus ediciones sin escena. Luego, restablecer. Después de eso, muestra tus cambios ocultos y agrégalos.
```sh
$ git stash -k
$ git reset --hard
$ git stash pop
$ git add -A
```
## Ediciones sin escena
### Deseo mover mis ediciones sin escena a una nueva rama
```sh
$ git checkout -b mi-rama
```
### Deseo mover mis ediciones sin escena a una rama diferente existente
```sh
$ git stash
$ git checkout my-rama
$ git stash pop
```
### Quiero descartar mis cambios locales no confirmados (en escena y sin escena)
Si deseas descartar todos los cambios organizados y no supervisados locales, puede hacer esto:
```sh
(mi-rama) $ git reset --hard
# o
(main) $ git checkout -f
```
Esto borrará todos los archivos que hayas organizado con `git add`:
```sh
$ git reset
```
Esto revertirá todos los cambios locales no confirmados (se debe ejecutar en la raíz del repositorio):
```sh
$ git checkout .
```
También puedes revertir cambios no confirmados a un archivo o directorio en particular:
```sh
$ git checkout [some_dir | file.txt]
```
Otra forma de revertir todos los cambios no confirmados (más largo de escribir, pero funciona desde cualquier subdirectorio):
```sh
$ git reset --hard HEAD
```
Esto eliminará todos los archivos locales sin seguimiento, por lo que solo se conservarán los archivos rastreados por Git:
```sh
$ git clean -fd
```
`-x` también eliminará todos los archivos ignorados.
### Quiero descartar cambios específicos no planificados
Cuando desees deshacerse de algunos, pero no de todos los cambios en su copia de trabajo.
Verifica los cambios no deseados, mantén buenos cambios.
```sh
$ git checkout -p
# Responde y a todos los cambios que desea eliminar
```
Otra estrategia implica el uso de `stash`. Guarda todos los buenos cambios, restablece la copia de trabajo y vuelve a aplicar los buenos cambios.
```sh
$ git stash -p
# Selecciona todos los cambios que desea guardar
$ git reset --hard
$ git stash pop
```
Alternativamente, oculta sus cambios no deseados, y luego deja el escondite.
```sh
$ git stash -p
# Selecciona todos los cambios que no quiere guardar
$ git stash drop
```
### Quiero descartar archivos específicos no escaneados
Cuando desees deshacerse de un archivo específico en su copia de trabajo.
```sh
$ git checkout myFile
```
Alternativamente, para descartar varios archivos en su copia de trabajo, enumera todos.
```sh
$ git checkout myFirstFile mySecondFile
```
### Deseo descartar solo mis cambios locales sin escenario
Cuando desees deshacerse de todos sus cambios no confirmados locales sin confirmar
```sh
$ git checkout .
```
### Quiero descartar todos mis archivos sin seguimiento
Cuando desees deshacerse de todos sus archivos sin seguimiento
```sh
$ git clean -f
```
## Ramas
### Quiero enumerar todas las ramas
Enumerar ramas locales
```sh
$ git branch
```
Enumerar ramas remotas
```sh
$ git branch -r
```
Listar todas las ramas (tanto locales como remotas)
```sh
$ git branch -a
```
### Crear una rama desde una confirmación
```sh
$ git checkout -b
```
### Hice pull de / en la rama incorrecta
Esta es otra oportunidad de usar `git reflog` para ver dónde apuntó el HEAD antes del mal tirón.
```sh
(main) $ git reflog
ab7555f HEAD @ {0}: pull-wrong-branch: Fast-forward
c5bc55a HEAD @ {1}: checkout: checkout message goes here
```
Simplemente restablece tu rama al commit deseado:
```sh
$ git reset --hard c5bc55a
```
Hecho.
### Quiero descartar confirmaciones locales para que mi rama sea la misma que la del servidor
Confirma que no ha enviado sus cambios al servidor.
`git status` debe mostrar cuántos commits tienes adelantado al origin:
```sh
(my-branch)$ git status
# On branch my-branch
# Your branch is ahead of 'origin/my-branch' by 2 commits.
# (use "git push" to publish your local commits)
#
```
Una forma de reiniciar para hacer coincidir el origin (tener lo mismo que lo que está en el control remoto) es hacer esto:
```sh
(my-branch) $ git reset --hard origin/my-branch
```
### Hice commit a main en lugar de una nueva rama
Crea la nueva rama mientras permaneces en main:
```sh
(main) $ git branch my-branch
```
Restablece la rama main al commit anterior:
```sh
(main) $ git reset --hard HEAD ^
```
`HEAD ^` es la abreviatura de `HEAD ^ 1`. Esto representa el primer padre de `HEAD`, del mismo modo` HEAD ^ 2` representa el segundo padre del commit (las fusiones pueden tener 2 padres).
Ten en cuenta que `HEAD ^ 2` ** no ** es lo mismo que` HEAD ~ 2` (vea [este enlace] (http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde) para más información).
Alternativamente, si no quieres usar `HEAD ^`, averigüe a qué hash de confirmación quieres establecer su rama principal (`git log` debería ser el truco). Luego reinicia a ese hash. `git push` se asegurará de que este cambio se refleje en su control remoto.
Por ejemplo, si el hash del commit en el que se supone que está su rama principal es `a13b85e`:
```sh
(main) $ git reset --hard a13b85e
HEAD is now at a13b85e
```
Verifique la nueva rama para continuar trabajando:
```sh
(main) $ git checkout my-branch
```
### Quiero mantener todo el archivo de otro ref-ish
Supongamos que tienes un pico activo (ver nota), con cientos de cambios. Todo está funcionando. Ahora, te comprometes con otra rama para guardar ese trabajo:
```sh
(solución) $ git add -A && git commit -m "Agregando todos los cambios desde este pico en un gran compromiso."
```
Cuando desees colocarlo en una rama (tal vez función, tal vez `develop`), te interesa conservar archivos completos. Quieres dividir tu gran compromiso en otros más pequeños.
Digamos que tienes:
* branch `solution`, con la solución para tu spike. Uno por delante de `develop`.
* branch `develop`, donde desea agregar sus cambios.
Puedes resolverlo llevando los contenidos a tu rama:
```sh
(develop) $ git checkout solution - file1.txt
```
Esto obtendrá los contenidos de ese archivo en la 'solución' de la rama a su rama `develop`:
```sh
# On branch develop
# Your branch is up-to-date with 'origin/develop'.
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: file1.txt
```
Entonces, haz un commit como de costumbre.
Nota: Las soluciones de Spike están hechas para analizar o resolver el problema. Estas soluciones se utilizan para la estimación y se descartan una vez que todos obtienen una visualización clara del problema. ~ [Wikipedia] (https://en.wikipedia.org/wiki/Extreme_programming_practices).
### Realicé varios commits en una sola rama que debería estar en diferentes ramas
Digamos que estás en tu rama principal. Al ejecutar `git log`, verás que ha realizado dos commits:
```sh
(main) $ git log
commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee
Date: Tue Jul 22 15:39:27 2014 -0400
Bug #21 - Added CSRF protection
commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee
Date: Tue Jul 22 15:39:12 2014 -0400
Bug #14 - Fixed spacing on title
commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose
Date: Tue Jul 21 01:12:48 2014 -0400
First commit
```
Toma nota de nuestros hashes de confirmación para cada error (`e3851e8` para # 21,` 5ea5173` para # 14).
Primero, restablece la rama principal al commit correcto (`a13b85e`):
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
Ahora, puedes crear una nueva rama para la rama bug # 21:
```sh
(main) $ git checkout -b 21
(21) $
```
Ahora, * selecciona con precisión * el commit para el error # 21 en la parte superior de la rama. Eso significa que aplicarás ese commit, y solo ese commit, directamente sobre el HEAD que estés.
```sh
(21) $ git cherry-pick e3851e8
```
En este punto, existe la posibilidad de que haya conflictos. Consulta la sección ** Hubo conflictos ** (# conflicto de fusión) en la [sección interactiva de rebase más arriba] (# interactive-rebase) para saber cómo resolver conflictos.
Ahora creamos una nueva rama para el error # 14, también basado en el main
```sh
(21) $ git checkout main
(main) $ git checkout -b 14
(14) $
```
Y finalmente, vamos a seleccionar el compromiso para el error # 14:
```sh
(14) $ git cherry-pick 5ea5173
```
### Quiero eliminar las ramas locales que se eliminaron en sentido ascendente
Una vez que fusiona una solicitud de extracción en GitHub, le da la opción de eliminar la rama fusionada en su fork. Si no tiene planeado seguir trabajando en la rama, es más limpio eliminar las copias locales de la rama para que no termine complicando su proceso de pago con muchas ramas obsoletas.
```sh
$ git fetch -p upstream
```
donde, 'ascendente' es el control remoto desde el que desea recuperar.
### Accidentalmente borré mi rama
Si empujas regularmente hacia el control remoto, deberías estar seguro la mayor parte del tiempo. Pero aún así a veces puede terminar borrando sus ramaes. Digamos que creamos una rama y creamos un nuevo archivo:
```sh
(main)$ git checkout -b my-branch
(my-branch)$ git branch
(my-branch)$ touch foo.txt
(my-branch)$ ls
README.md foo.txt
```
Vamos a agregarlo y hacer el commit.
```sh
(my-branch)$ git add .
(my-branch)$ git commit -m 'foo.txt added'
(my-branch)$ foo.txt added
1 files changed, 1 insertions(+)
create mode 100644 foo.txt
(my-branch)$ git log
commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
Author: siemiatj
Date: Wed Jul 30 00:34:10 2014 +0200
foo.txt added
commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson
Date: Tue Jul 29 13:14:46 2014 -0400
Fixes #6: Force pushing after amending commits
```
Ahora estamos volviendo a "main" y "accidentalmente" eliminando nuestra rama.
```sh
(my-branch)$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
(main)$ git branch -D my-branch
Deleted branch my-branch (was 4e3cd85).
(main)$ echo oh noes, deleted my branch!
oh noes, deleted my branch!
```
En este punto, debe familiarizarse con 'reflog', un registrador actualizado. Almacena el historial de todas las acciones en el repositorio.
```
(main)$ git reflog
69204cd HEAD@{0}: checkout: moving from my-branch to main
4e3cd85 HEAD@{1}: commit: foo.txt added
69204cd HEAD@{2}: checkout: moving from main to my-branch
```
Como puede ver, hemos confirmado el hash de nuestra rama eliminada. Veamos si podemos restaurar nuestra rama eliminada.
```sh
(main)$ git checkout -b my-branch-help
Switched to a new branch 'my-branch-help'
(my-branch-help)$ git reset --hard 4e3cd85
HEAD is now at 4e3cd85 foo.txt added
(my-branch-help)$ ls
README.md foo.txt
```
Voila! Recuperamos nuestro archivo eliminado. `git reflog` también es útil cuando el rebase es terriblemente incorrecto.
### Quiero eliminar una rama
Para eliminar una rama remota:
```sh
(main) $ git push origin --delete my-branch
```
También puedes hacer:
```sh
(main) $ git push origin: my-branch
```
Para eliminar una rama local:
```sh
(main) $ git branch -d my-branch
```
Para eliminar una rama local que * no * se ha fusionado con la rama actual o una cadena ascendente:
```sh
(main) $ git branch -D my-branch
```
### Quiero eliminar varias ramas
Supongamos que quiere eliminar todas las ramas que comienzan con `fix /`:
```sh
(main) $ git rama | grep 'fix /' | xargs git branch -d
```
### Quiero cambiar el nombre de una rama
Para cambiar el nombre de la rama actual (local):
```sh
(main) $ git branch -m new-name
```
Para cambiar el nombre de una rama diferente (local):
```sh
(main) $ git branch -m old-name new-name
```
### Quiero hacer checkout en una rama remota en la que alguien más está trabajando
Primero, busca todas las ramas desde el control remoto:
```sh
(main)$ git fetch --all
```
Digamos que quieres hacer checkout a `daves` desde el repositorio remoto.
```sh
(main)$ git checkout --track origin/daves
Branch daves set up to track remote branch daves from origin.
Switched to a new branch 'daves'
```
(`--track` es la abreviatura de 'git checkout -b [branch] [remotename] / [branch]`)
Esto le dará una copia local de la rama `daves`, y cualquier actualización que se haya enviado también se mostrará de forma remota.
### Quiero crear una nueva rama remota desde la actual local
```sh
$ git push
```
Si también desea establecer esa rama remota como cadena arriba para la actual, use lo siguiente:
```sh
$ git push -u
```
Con el modo `upstream` y el modo `simple` (predeterminado en Git 2.0) de la configuración `push.default`, el siguiente comando empujará la bifurcación actual con respecto a la bifurcación remota que se ha registrado previamente con `-u `:
```sh
$ git push
```
El comportamiento de los otros modos de ```git push``` se describe en el documento de push.default.
### Quiero configurar una rama remota como upstream para una rama local
Puedes establecer una rama remota como ascendente para la sucursal local actual usando:
```sh
$ git branch --set-upstream-to [remotename]/[branch]
# or, using the shorthand:
$ git branch -u [remotename]/[branch]
```
Para establecer la rama remota ascendente para otra rama local:
```sh
$ git branch -u [remotename] / [branch] [local-branch]
```
### Quiero configurar mi HEAD para rastrear la rama remota predeterminada
Al verificar tus ramas remotas, puedes ver en qué rama remota está rastreando el HEAD. En algunos casos, esta no es la rama deseada.
```sh
$ git branch -r
origin/HEAD -> origin/gh-pages
origin/main
```
Cambiar `origin / HEAD` para rastrear` origin / main`, puedes ejecutar este comando:
```sh
$ git remote set-head origin --auto
origin/HEAD set to main
```
## Rebasing y Merging
### Quiero deshacer rebase/merge
Es posible que hayas fusionado o rediseñado tu rama actual con una rama incorrecta, o que no puedas resolverlo o finalizar el proceso de rebase/merge. Git guarda el puntero original HEAD en una variable llamada ORIG_HEAD antes de realizar operaciones peligrosas, por lo que es sencillo recuperar la rama en el estado anterior al rebase/merge.
```sh
(my-branch)$ git reset --hard ORIG_HEAD
```
### Hice rebase, pero no quiero forzar el push
Desafortunadamente, debes forzar el push, si deseas que esos cambios se reflejen en la rama remota. Esto se debe a que ha cambiado el historial. La rama remota no aceptará cambios a menos que fuerce la inserción. Esta es una de las principales razones por las que muchas personas usan un flujo de trabajo de merge, en lugar de un flujo de trabajo de reordenación: los equipos grandes pueden tener problemas con el impulso de los desarrolladores. Usa esto con precaución. Una forma más segura de utilizar rebase no es reflejar los cambios en la rama remota, sino hacer lo siguiente:
```sh
(main)$ git checkout my-branch
(my-branch)$ git rebase -i main
(my-branch)$ git checkout main
(main)$ git merge --ff-only my-branch
```
Para obtener más información, consulte [este thread SO] (https://stackoverflow.com/questions/11058312/how-can-i-use-git-rebase-without-requiring-a-forced-push).
### Necesito combinar commits
Supongamos que estás trabajando en una rama que es / se convertirá en un pull request contra main. En el caso más simple, cuando todo lo que quiere hacer es combinar todos los commits en uno solo y no le importa cometer timestamps, puedes restablecer y volver a hacer el commit. Asegúrate de que la rama principal esté actualizada y de que se hayan confirmado todos los cambios, luego:
```sh
(my-branch)$ git reset --soft main
(my-branch)$ git commit -am "New awesome feature"
```
Si deseas más control y también conservar las marcas de tiempo, debe hacer algo que se llame un rebase interactivo:
```sh
(my-branch)$ git rebase -i main
```
Si no estás trabajando contra otra rama tendrás que volver a establecer una base relativa a tu `HEAD`. Si quieres aplastar las últimas 2 confirmaciones, por ejemplo, tendrás que volver a calcular contra `HEAD ~ 2`. Para los últimos 3, `HEAD ~ 3`, etc.
```sh
(main)$ git rebase -i HEAD~2
```
Después de ejecutar el comando de rebase interactivo, verás algo como esto en tu editor de texto:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
pick b729ad5 fixup
pick e3851e8 another fix
# Rebase 8074d12..b729ad5 onto 8074d12
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```
Todas las líneas que comienzan con `#` son comentarios, no afectarán a tu rebase.
Reemplaza los comandos `pick` con cualquiera en la lista anterior, y también puedes eliminar commits eliminando las líneas correspondientes.
Por ejemplo, si deseas **dejar el commit más antiguo (el primero) solo y combinar todos los siguientes commits con el segundo más antiguo**, debes editar la letra junto a cada commit, excepto la primera y la segunda para decir `f` :
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
f b729ad5 fixup
f e3851e8 another fix
```
Si deseas combinar estos commits **y cambiar el nombre del commit**, también debes agregar una `r` junto al segundo commit o simplemente usar` s` en lugar de `f`:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
s b729ad5 fixup
s e3851e8 another fix
```
A continuación, puedes cambiar el nombre del commit en el siguiente mensaje de texto que aparece.
```vim
Newer, awesomer features
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 8074d12
# You are currently editing a commit while rebasing branch 'main' on '8074d12'.
#
# Changes to be committed:
# modified: README.md
#
```
Si todo tiene éxito, deberías ver algo como esto:
```sh
(main)$ Successfully rebased and updated refs/heads/main.
```
#### Estrategia de merge segura
`--no-commit` realiza el merge, pero simula que la combinación falló y no se confirma automáticamente, lo que le da al usuario la oportunidad de inspeccionar y modificar aún más el resultado de la combinación antes de realizar la tarea. `no-ff` mantiene la evidencia de que alguna vez existió una rama de características, manteniendo la historia del proyecto consistente.
```sh
(main)$ git merge --no-ff --no-commit my-branch
```
#### Necesito fusionar una rama en un solo commit
```sh
(main)$ git merge --squash my-branch
```
#### Quiero combinar solo los commits sin haber hecho push
A veces tiene varios commits en progreso que deseas combinar antes de hacer push. No deseas combinar accidentalmente ningún commit que ya haya sido pusheado porque otra persona ya haya realizado commits que les hagan referencia.
```sh
(main)$ git rebase -i @{u}
```
Esto hará una base de datos interactiva que enumera solo los commits que aún no has enviado, por lo que será seguro reordenar / arreglar / aplastar cualquier elemento de la lista.
#### Necesito abortar el merge
A veces, la fusión puede producir problemas en ciertos archivos, en esos casos podemos usar la opción `abort` para abortar el proceso actual de resolución de conflictos, y tratar de reconstruir el estado previo a la fusión.
```sh
(my-branch)$ git merge --abort
```
Este comando está disponible desde la versión de Git> = 1.7.4
### Comprobar si se combinan todos los commits de un branch
Para comprobar si todos los commits de una rama se fusionan en otra, debe distinguir las cabeceras (o los commits) de esas ramas:
```sh
(main)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
```
Esto te dirá si hay commits en una pero no en la otra, y te dará una lista de las no compartidas entre las ramas. Otra opción es hacer esto:
```sh
(main)$ git log main ^feature/120-on-scroll --no-merges
```
### Posibles problemas con rebase interactivos
#### La pantalla de edición de rebase dice 'noop'
Si estás viendo esto:
```
noop
```
Eso significa que estás tratando de volver a establecer una base contra una rama que está en un commit idéntico, o está *delante* de tu rama actual. Puedes probar:
* asegurándote de que tu rama principal esté donde debería estar
* rebase contra `HEAD ~ 2` o anterior a su lugar
#### Hubo conflictos
Si no puedes completar correctamente el rebase, es posible que tengas que resolver conflictos.
Primero ejecuta `git status` para ver qué archivos tienen conflictos en ellos:
```sh
(my-branch)$ git status
On branch my-branch
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
both modified: README.md
```
En este ejemplo, `README.md` tiene conflictos. Abre ese archivo y busca lo siguiente:
```vim
<<<<<<< HEAD
some code
=========
some code
>>>>>>> new-commit
```
Tendrás que resolver las diferencias entre el código que se agregó en tu nuevo commit (en el ejemplo, todo desde la línea media hasta `new-commit ') y tu' HEAD '.
Si deseas conservar la versión del código de una rama, puedes usar `--us` o` --theirs`:
```sh
(main*)$ git checkout --ours README.md
```
- Cuando haces *merge*, usa `--ours` para mantener los cambios de la rama local, o` --theirs` para mantener los cambios de la otra rama.
- Cuando haces *rebase*, usa `--theirs` para mantener los cambios de la rama local, o` --ours` para mantener los cambios de la otra rama. Para obtener una explicación de este intercambio, consulte [esta nota en la documentación de Git] (https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---merge).
Si las fusiones son más complicadas, puede usar un editor visual diff:
```sh
(main*)$ git mergetool -t opendiff
```
Después de haber resuelto todos los conflictos y probado el código, `git add` los archivos que has cambiado, y luego continúa el rebase con` git rebase --continue`
```sh
(my-branch)$ git add README.md
(my-branch)$ git rebase --continue
```
Si después de resolver todos los conflictos terminas con un árbol idéntico a lo que era antes del commit, necesitas `git rebase --skip` en su lugar.
Si en algún momento deseas detener todo el rebase y volver al estado original de tu rama, puedes hacer:
```sh
(my-branch)$ git rebase --abort
```
## Stash
### Usar stash en todos los cambios
Para esconder todas las ediciones de tu directorio de trabajo
```sh
$ git stash
```
Si también quiere esconder archivos sin seguimiento, use la opción `-u`.
```sh
$ git stash -u
```
### Usar stash para archivos específicos
Para esconder solo un archivo de tu directorio de trabajo
```sh
$ git stash push working-directory-path/filename.ext
```
Para esconder varios archivos de tu directorio de trabajo
```sh
$ git stash push working-directory-path/filename1.ext working-directory-path/filename2.ext
```
### Usar stash con un mensaje
```sh
$ git stash save
```
### Aplicar un stash específico de la lista
Primero revisa la lista de stash con mensaje usando
```sh
$ git stash list
```
Luego selecciona un stash específico de la lista usando
```sh
$ git stash apply "stash@{n}"
```
Aquí, 'n' indica la posición del stash en la pila. El stash más alto será la posición 0.
## Búsqueda
### Quiero encontra un string en algún commit
Para encontrar un determinada string que se introdujo en cualquier commit, puedes usar la siguiente estructura:
```sh
$ git log -S "string to find"
```
Parámetros comunes:
* `--source` significa mostrar el nombre de la referencia dada en la línea de comando por la cual se llegó a cada commit.
* `--all` significa comenzar desde cada rama.
* `--reverse` imprime en orden inverso, significa que mostrará el primer commit que realizó el cambio.
### Quiero buscar por autor / committer
Para encontrar todos los commits por autor / committer, puedes usar:
```sh
$ git log --author=
$ git log --committer=
```
Ten en cuenta que autor y committer no son lo mismo. El `--autor` es la persona que originalmente escribió el código; por otro lado, el `--committer` es la persona que subió el código en nombre del autor original.
### Quiero enumerar commits que contienen archivos específicos
Para encontrar todos los commits que contienen un archivo específico, puedes usar:
```sh
$ git log --
```
Por lo general, debes especificar una ruta exacta, pero también puede usar comodines en la ruta y el nombre del archivo:
```sh
$ git log -- **/*.js
```
Al usar comodines, es útil informar `--name-status` para ver la lista de archivos comprometidos:
```sh
$ git log --name-status -- **/*.js
```
### Encontrar una etiqueta donde se hace referencia a un commit
Para encontrar todas las etiquetas que contienen un commit específico:
```sh
$ git tag --contains
```
## Submódulos
### Clonar todos los submódulos
```sh
$ git clone --recursive git://github.com/foo/bar.git
```
Si ya fue clonado
```sh
$ git submodule update --init --recursive
```
### Remover un submódulo
Crear un submódulo es bastante simple, pero eliminarlos no. Los comandos que necesitas son:
```sh
$ git submodule deinit submodulename
$ git rm submodulename
$ git rm --cached submodulename
$ rm -rf .git/modules/submodulename
```
## Objetos diversos
### Restaurar un archivo eliminado
Primero encuentra el commit donde el archivo existió por última vez:
```sh
$ git rev-list -n 1 HEAD -- filename
```
Luego hazle checkout a ese archivo
```
git checkout deletingcommitid^ -- filename
```
### Eliminar una etiqueta
```sh
$ git tag -d
$ git push :refs/tags/
```
### Recuperar una etiqueta eliminada
Si deseas recuperar una etiqueta que ya fue eliminada, puede hacerlo siguiendo estos pasos: Primero, debe encontrar la etiqueta inalcanzable:
```sh
$ git fsck --unreachable | grep tag
```
Toma nota del hash de la etiqueta. Luego, restaura la etiqueta eliminada con la siguiente, haciendo uso de [`git update-ref`] (https://git-scm.com/docs/git-update-ref):
```sh
$ git update-ref refs/tags/
```
Tu etiqueta ahora debería haber sido restaurada.
### Patch eliminado
Si alguien envió un pull request en GitHub, pero luego eliminó el fork original, no podrá clonar su repositorio ni usar `git am` como [.diff, .patch] (https://github.com / blog / 967-github-secrets) las URL no están disponibles. Pero puedes verificar el PR utilizando las [referencias especiales de GitHub] (https://gist.github.com/piscisaureus/3342247). Para recuperar el contenido de PR # 1 en una nueva rama llamada pr_1:
```sh
$ git fetch origin refs/pull/1/head:pr_1
From github.com:foo/bar
* [new ref] refs/pull/1/head -> pr_1
```
### Exportar un repositorio como un archivo Zip
```sh
$ git archive --format zip --output /full/path/to/zipfile.zip main
```
## Seguimiento de archivos
### Quiero cambiar el uso de mayúsculas de un nombre de archivo, sin cambiar el contenido del archivo
```sh
(main)$ git mv --force myfile MyFile
```
### Quiero sobrescribir los archivos locales cuando hago un git pull
```sh
(main)$ git fetch --all
(main)$ git reset --hard origin/main
```
### Quiero eliminar un archivo de Git pero mantener el archivo
```sh
(main)$ git rm --cached log.txt
```
### Quiero revertir un archivo a una revisión específica
Suponiendo que el hash del commit que deseas es c5f567:
```sh
(main)$ git checkout c5f567 -- file1/to/restore file2/to/restore
```
Si desea volver a los cambios realizados solo 1 commit antes de c5f567, pase el hash de confirmación como c5f567 ~ 1:
```sh
(main)$ git checkout c5f567~1 -- file1/to/restore file2/to/restore
```
## Configuración
### Quiero agregar alias para algunos comandos de Git
En OS X y Linux, tu archivo de configuración de git se almacena en ```~ / .gitconfig```. He agregado algunos alias de ejemplo que uso como accesos directos (y algunos de mis errores comunes) en la sección `` `[alias]` `` como se muestra a continuación:
```vim
[alias]
a = add
amend = commit --amend
c = commit
ca = commit --amend
ci = commit -a
co = checkout
d = diff
dc = diff --changed
ds = diff --staged
f = fetch
loll = log --graph --decorate --pretty=oneline --abbrev-commit
m = merge
one = log --pretty=oneline
outstanding = rebase -i @{u}
s = status
unpushed = log @{u}
wc = whatchanged
wip = rebase -i @{u}
zap = fetch -p
```
### Quiero agregar un directorio vacío a mi repositorio
¡No puedes! Git no es compatible con esto, pero hay un truco. Puede crear un archivo .gitignore en el directorio con los siguientes contenidos:
```
# Ignore everything in this directory
*
# Except this file
!.gitignore
```
Otra convención común es crear un archivo vacío en la carpeta, titulado .gitkeep.
```sh
$ mkdir mydir
$ touch mydir/.gitkeep
```
También puedes nombrar el archivo como .keep, en cuyo caso la segunda línea de arriba sería ```touch mydir / .keep```
### Quiero guardar en caché un nombre de usuario y contraseña para un repositorio
Es posible que tengas un repositorio que requiera autenticación. En ese caso, puedes guardar en caché un nombre de usuario y contraseña para que no tenga que ingresarlos en cada push / pull. Credential helper puede hacer esto por ti.
```sh
$ git config --global credential.helper cache
# Set git to use the credential memory cache
```
```sh
$ git config --global credential.helper 'cache --timeout=3600'
# Set the cache to timeout after 1 hour (setting is in seconds)
```
### Quiero hacer que Git ignore los permisos y cambios en el modo de archivo
```sh
$ git config core.fileMode false
```
Si deseas que este sea el comportamiento predeterminado para los usuarios que han iniciado sesión, utilice:
```sh
$ git config --global core.fileMode false
```
## No tengo idea de lo que hice mal
Entonces, estás jodido, "reiniciaste" algo, o fusionaste la rama incorrecta, o forzaste empujar y ahora no puedes encontrar tus commits. Sabes, en algún momento, estabas bien y quieres volver a un estado en el que estabas.
Para esto está hecho `git reflog`. `reflog` realiza un seguimiento de los cambios en la punta de una rama, incluso si esa sugerencia no está referenciada por una rama o una etiqueta. Básicamente, cada vez que HEAD cambia, se agrega una nueva entrada al reflog. Esto solo funciona para los repositorios locales, lamentablemente, y solo rastrea los movimientos (no los cambios a un archivo que no fueron grabados en ninguna parte, por ejemplo).
```sh
(main)$ git reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
```
El reflog anterior muestra una salida desde main a la rama 2.2 y viceversa. A partir de ahí, hay un restablecimiento completo de un commit más antiguo. La última actividad se representa en la parte superior con la etiqueta 'HEAD @ {0} `.
Si resulta que retrocedió accidentalmente, el reflog contendrá el main de un commit apuntado a (0254ea7) antes de que accidentalmente soltara 2 commits.
```sh
$ git reset --hard 0254ea7
```
Usando `git reset` es posible cambiar el main al commit que era antes. Esto proporciona una red de seguridad en caso de que la historia se haya cambiado accidentalmente.
(copiado y editado desde [Fuente] (https://www.atlassian.com/git/tutorials/rewriting-history/git-reflog)).
# Otros recursos
## Libros
* [Pro Git](https://git-scm.com/book/en/v2) - Excelente libro de Scott Chacon y Ben Straub sobre Git
* [Git Internals](https://github.com/pluralsight/git-internals-pdf) - Otro excelente libro de Scott Chacon sobre Git
## Tutoriales
* [Tutorial de Git de Atlassian](https://www.atlassian.com/git/tutorials) Obtenga Git a la derecha con tutoriales de principiante a avanzado.
* [Aprende la ramificación de Git](https://learngitbranching.js.org/) Tutorial interactivo de ramificación / fusión / rebase basado en la web
* [Obteniendo solidez en Git rebase vs. merge](https://medium.com/@porteneuve/getting-solid-at-git-rebase-vs-merge-4fa1a48c53aa)
* [git-workflow](https://github.com/asmeurer/git-workflow) - [Aaron Meurer](https://github.com/asmeurer) 'Howto sobre el uso de Git para contribuir a repositorios de código abierto
* [GitHub como flujo de trabajo](https://hugogiraudel.com/2015/08/13/github-as-a-workflow/) - Una interesante idea sobre el uso de GitHub como flujo de trabajo, particularmente con los PR vacíos
* [Githug](https://github.com/Gazler/githug) - Un juego para aprender más flujos de trabajo de Git
## Scripts y herramientas
* [firstaidgit.io](http://firstaidgit.io/) Una selección de búsqueda de las preguntas más frecuentes de Git
* [git-extra-commands](https://github.com/unixorn/git-extra-commands) - una colección de scripts extra útiles de Git
* [git-extras](https://github.com/tj/git-extras) - Utilidades de GIT - resumen de repo, repl, población de registro de cambios, porcentajes de confirmación de autor y más
* [git-fire](https://github.com/qw3rtman/git-fire) - git-fire es un plugin de Git que ayuda en caso de una emergencia al agregar todos los archivos actuales, comprometerse y empujar a un nuevo rama (para evitar conflictos de combinación).
* [git-tips](https://github.com/git-tips/tips) - Consejos pequeños de Git
* [git-town](https://github.com/Originate/git-town) - Soporte genérico de alto nivel de flujo de trabajo de Git! http://www.git-town.com
## Clientes GUI
* [GitKraken](https://www.gitkraken.com/) - El cliente francamente lujoso de Git, para Windows, Mac y Linux
* [git-cola](https://git-cola.github.io/) - otro cliente de Git para Windows y OS X
* [GitUp](https://github.com/git-up/GitUp) - Una interfaz gráfica de usuario nueva que tiene algunas formas muy dogmáticas de lidiar con las complicaciones de Git
* [gitx-dev](https://rowanj.github.io/gitx/) - otro cliente gráfico de Git para OS X
* [Sourcetree](https://www.sourcetreeapp.com/) - Simplicity se une a la potencia en una hermosa y gratuita GUI de Git. Para Windows y Mac.
* [Tower](https://www.git-tower.com/) - cliente gráfico de Git para OS X (pago)
* [tig](https://jonas.github.io/tig/) - interfaz de texto en modo terminal para Git
* [Magit](https://magit.vc/) - Interfaz para Git implementada como un paquete de Emacs.
* [GitExtensions](https://github.com/gitextensions/gitextensions): una extensión de shell, un plugin de Visual Studio 2010-2015 y una herramienta de repositorio de Git independiente.
* [Fork](https://git-fork.com/) - un cliente de Git rápido y amigable para Mac (beta)
* [gmaster](https://gmaster.io/) - un cliente de Git para Windows que tiene combinación de 3 vías, analizan refactores, diferencias semánticas y fusión (beta)
* [gitk](https://git-scm.com/docs/gitk) - un cliente de Git para Linux para permitir una vista simple del estado de repos.
================================================
FILE: README_fr.md
================================================
# Règles de vol pour Git
🌍
*[English](README.md) ∙ [Español](README_es.md) ∙ [Русский](README_ru.md) ∙ [繁體中文](README_zh-TW.md) ∙ [简体中文](README_zh-CN.md) ∙ [한국어](README_kr.md) ∙ [Tiếng Việt](README_vi.md) ∙ [Français](README_fr.md) ∙ [日本語](README_ja.md)*
#### C'est quoi des "règles de vol" ?
Un guide pour les astronautes (pour les développeur·euse·s utilisant Git, dans le cas présent) sur quoi faire quand les choses se passent mal.
> *Les règles de vol* représentent l'ensemble des connaissances durement acquises consignées dans des manuels répertoriant, étape par étape, la procédure à suivre si telle chose se produit et pourquoi. Il s'agit essentiellement de procédures d'exploitation standard extrêmement détaillées et spécifiques à chaque scénario. [...]
> La NASA a recueilli nos faux pas, nos désastres et nos solutions depuis le début des années 60, lorsque les équipes terrestres de l'ère Mercury ont commencé à rassembler les "leçons tirées" dans un recueil qui répertorie désormais des milliers de situations problématiques, des pannes de moteur aux poignées de trappes cassées en passant par les bugs informatiques, et leurs solutions.
— Chris Hadfield, *Guide d'un astronaute pour la vie sur Terre*.
#### Conventions pour ce document
Pour des raisons de clarté tous les exemples dans ce document utilisent une invite de commande bash personnalisée dans le but d'indiquer la branche actuelle et s'il y a des modifications indexées ou non. La branche se trouve entre parenthèses, et un `*` après le nom de la branche indique des modifications indexées.
Toutes les commandes devraient fonctionner pour les versions de Git au moins supérieures à la 2.13.0. Visitez le [site de Git](https://www.git-scm.com/) pour mettre à jour votre version de git locale.
[](https://gitter.im/k88hudson/git-flight-rules?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Dépôts](#d%C3%A9p%C3%B4ts)
- [Je veux initialiser un dépôt local](#je-veux-initialiser-un-d%C3%A9p%C3%B4t-local)
- [Je veux cloner un dépôt distant](#je-veux-cloner-un-d%C3%A9p%C3%B4t-distant)
- [Éditer des Commits](#%C3%89diter-des-commits)
- [Qu'est-ce que je viens de commiter ?](#quest-ce-que-je-viens-de-commiter-)
- [J'ai commis une erreur dans un message de commit](#jai-commis-une-erreur-dans-un-message-de-commit)
- [J'ai commit avec le mauvais nom et email dans les configurations](#jai-commit-avec-le-mauvais-nom-et-email-dans-les-configurations)
- [Je veux retirer un fichier du commit précédent](#je-veux-retirer-un-fichier-du-commit-pr%C3%A9c%C3%A9dent)
- [Je veux supprimer ou retirer mon dernier commit](#je-veux-supprimer-ou-retirer-mon-dernier-commit)
- [Supprimer/retirer un commit arbitraire](#supprimerretirer-un-commit-arbitraire)
- [J'ai essayé de pousser un commit modifié vers le dépôt distant, mais j'ai eu un message d'erreur](#jai-essay%C3%A9-de-pousser-un-commit-modifi%C3%A9-vers-le-d%C3%A9p%C3%B4t-distant-mais-jai-eu-un-message-derreur)
- [J'ai fait un hard reset par accident, et je veux retrouver mes changements](#jai-fait-un-hard-reset-par-accident-et-je-veux-retrouver-mes-changements)
- [J'ai commité et poussé une fusion par accident](#jai-commit%C3%A9-et-pouss%C3%A9-une-fusion-par-accident)
- [J'ai commité et poussé des fichiers contenant des données sensibles par accident](#jai-commit%C3%A9-et-pouss%C3%A9-des-fichiers-contenant-des-donn%C3%A9es-sensibles-par-accident)
- [Indexation](#indexation)
- [J'ai besoin d'ajouter des modifications indexées sur le commit précédent](#jai-besoin-dajouter-des-modifications-index%C3%A9es-sur-le-commit-pr%C3%A9c%C3%A9dent)
- [Je veux indexer une partie d'un fichier, mais pas tout le fichier](#je-veux-indexer-une-partie-dun-fichier-mais-pas-tout-le-fichier)
- [Je veux ajouter les changements d'un fichier dans deux commits différents](#je-veux-ajouter-les-changements-dun-fichier-dans-deux-commits-diff%C3%A9rents)
- [Je veux indexer mes modifications indexées, et désindexer mes modifications indexées](#je-veux-indexer-mes-modifications-index%C3%A9es-et-d%C3%A9sindexer-mes-modifications-index%C3%A9es)
- [Modifications non indexées](#modifications-non-index%C3%A9es)
- [Je veux déplacer mes modifications non indexées vers une nouvelle branche](#je-veux-d%C3%A9placer-mes-modifications-non-index%C3%A9es-vers-une-nouvelle-branche)
- [Je veux déplacer mes modifications non indexées vers une branche différente existante](#je-veux-d%C3%A9placer-mes-modifications-non-index%C3%A9es-vers-une-branche-diff%C3%A9rente-existante)
- [Je veux me débarrasser de mes modifications locales non commitées (indexées et non-indexées)](#je-veux-me-d%C3%A9barrasser-de-mes-modifications-locales-non-commit%C3%A9es-index%C3%A9es-et-non-index%C3%A9es)
- [Je veux me débarrasser de modifications non-indexées spécifiques](#je-veux-me-d%C3%A9barrasser-de-modifications-non-index%C3%A9es-sp%C3%A9cifiques)
- [Je veux me débarrasser de fichiers non-indexés spécifiques](#je-veux-me-d%C3%A9barrasser-de-fichiers-non-index%C3%A9s-sp%C3%A9cifiques)
- [Je veux me débarrasser de mes modifications locales non-indexées uniquement](#je-veux-me-d%C3%A9barrasser-de-mes-modifications-locales-non-index%C3%A9es-uniquement)
- [Je veux me débarrasser de tous mes fichiers non suivis](#je-veux-me-d%C3%A9barrasser-de-tous-mes-fichiers-non-suivis)
- [Je veux désindexer un fichier indexé spécifique](#je-veux-d%C3%A9sindexer-un-fichier-index%C3%A9-sp%C3%A9cifique)
- [Branches](#branches)
- [Je veux lister toutes les branches](#je-veux-lister-toutes-les-branches)
- [Créer une branche à partir d'un commit](#cr%C3%A9er-une-branche-%C3%A0-partir-dun-commit)
- [J'ai pull depuis/vers la mauvaise branche](#jai-pull-depuisvers-la-mauvaise-branche)
- [Je veux supprimer mes commits locaux afin que ma branche soit pareille à celle sur le serveur](#je-veux-supprimer-mes-commits-locaux-afin-que-ma-branche-soit-pareille-%C3%A0-celle-sur-le-serveur)
- [J'ai commité sur main au lieu d'une nouvelle branche](#jai-commit%C3%A9-sur-main-au-lieu-dune-nouvelle-branche)
- [Je veux séparer tout un fichier d'un autre ref-ish](#je-veux-s%C3%A9parer-tout-un-fichier-dun-autre-ref-ish)
- [J'ai fait plusieurs commits sur une même branche qui auraient dû être sur plusieurs branches](#jai-fait-plusieurs-commits-sur-une-m%C3%AAme-branche-qui-auraient-d%C3%BB-%C3%AAtre-sur-plusieurs-branches)
- [Je veux supprimer mes branches locales qui ont été supprimée sur le dépôt distant](#je-veux-supprimer-mes-branches-locales-qui-ont-%C3%A9t%C3%A9-supprim%C3%A9e-sur-le-d%C3%A9p%C3%B4t-distant)
- [J'ai supprimé ma branche par accident](#jai-supprim%C3%A9-ma-branche-par-accident)
- [Je veux supprimer une branche](#je-veux-supprimer-une-branche)
- [Je veux supprimer plusieurs branches](#je-veux-supprimer-plusieurs-branches)
- [Je veux renommer une branche](#je-veux-renommer-une-branche)
- [Je veux me déplacer sur une branche distante sur laquelle quelqu'un est en train de travailler](#je-veux-me-d%C3%A9placer-sur-une-branche-distante-sur-laquelle-quelquun-est-en-train-de-travailler)
- [Je veux créer une nouvelle branche distante à partir de celle en locale](#je-veux-cr%C3%A9er-une-nouvelle-branche-distante-%C3%A0-partir-de-celle-en-locale)
- [Je veux configurer une branche distante en tant qu'upstream pour une branche locale](#je-veux-configurer-une-branche-distante-en-tant-quupstream-pour-une-branche-locale)
- [Je veux configurer mon HEAD pour suivre la branche distante par défaut](#je-veux-configurer-mon-head-pour-suivre-la-branche-distante-par-d%C3%A9faut)
- [J'ai fait des modifications sur la mauvaise branche](#jai-fait-des-modifications-sur-la-mauvaise-branche)
- [Rebaser et fusionner](#rebaser-et-fusionner)
- [Je veux annuler un rebase/merge](#je-veux-annuler-un-rebasemerge)
- [J'ai rebase, mais je ne veux pas pousser de force](#jai-rebase-mais-je-ne-veux-pas-pousser-de-force)
- [J'ai besoin de combiner des commits](#jai-besoin-de-combiner-des-commits)
- [Stratégie de fusion sûre](#strat%C3%A9gie-de-fusion-s%C3%BBre)
- [J'ai besoin de fusionner deux branches en un seul commit](#jai-besoin-de-fusionner-deux-branches-en-un-seul-commit)
- [Je veux combiner les commits non poussés uniquement](#je-veux-combiner-les-commits-non-pouss%C3%A9s-uniquement)
- [J'ai besoin d'annuler la fusion](#jai-besoin-dannuler-la-fusion)
- [J'ai besoin de mettre à jour le commit parent de ma branche](#jai-besoin-de-mettre-%C3%A0-jour-le-commit-parent-de-ma-branche)
- [Vérifier si tous les commits d'une branche sont fusionnés](#v%C3%A9rifier-si-tous-les-commits-dune-branche-sont-fusionn%C3%A9s)
- [Problèmes possibles avec les rebase interactifs](#probl%C3%A8mes-possibles-avec-les-rebase-interactifs)
- [L'écran d'édition du rebase affiche 'noop'](#l%C3%A9cran-d%C3%A9dition-du-rebase-affiche-noop)
- [Il y a eu des conflits](#il-y-a-eu-des-conflits)
- [Remisage](#remisage)
- [Remiser toutes les modifications](#remiser-toutes-les-modifications)
- [Remiser des fichiers spécifiques](#remiser-des-fichiers-sp%C3%A9cifiques)
- [Remiser avec un message](#remiser-avec-un-message)
- [Appliquer un remisage spécifique de la liste](#appliquer-un-remisage-sp%C3%A9cifique-de-la-liste)
- [Rechercher](#rechercher)
- [Je veux rechercher une chaîne de caractères dans un commit](#je-veux-rechercher-une-cha%C3%AEne-de-caract%C3%A8res-dans-un-commit)
- [Je veux rechercher par auteur·trice/validateur·trice](#je-veux-rechercher-par-auteur%C2%B7tricevalidateur%C2%B7trice)
- [Je veux lister les commits ayant des fichiers spécifiques](#je-veux-lister-les-commits-ayant-des-fichiers-sp%C3%A9cifiques)
- [Trouver un tag où un commit est référencé](#trouver-un-tag-o%C3%B9-un-commit-est-r%C3%A9f%C3%A9renc%C3%A9)
- [Sous-modules](#sous-modules)
- [Cloner tous les sous-modules](#cloner-tous-les-sous-modules)
- [Retirer un sous-module](#retirer-un-sous-module)
- [Objets divers](#objets-divers)
- [Récupérer un fichier supprimé](#r%C3%A9cup%C3%A9rer-un-fichier-supprim%C3%A9)
- [Supprimer un tag](#supprimer-un-tag)
- [Récupérer un tag supprimé](#r%C3%A9cup%C3%A9rer-un-tag-supprim%C3%A9)
- [Patch supprimé](#patch-supprim%C3%A9)
- [Exporter un dépôt comme fichier Zip](#exporter-un-d%C3%A9p%C3%B4t-comme-fichier-zip)
- [Pousser une branche et un tag qui ont le même nom](#pousser-une-branche-et-un-tag-qui-ont-le-m%C3%AAme-nom)
- [Suivre des fichiers](#suivre-des-fichiers)
- [Je veux changer la capitalisation du nom d'un fichier, sans changer son contenu](#je-veux-changer-la-capitalisation-du-nom-dun-fichier-sans-changer-son-contenu)
- [Je veux écraser des fichiers locaux en faisant un git pull](#je-veux-%C3%A9craser-des-fichiers-locaux-en-faisant-un-git-pull)
- [Je veux retirer un fichier de Git mais garder le fichier](#je-veux-retirer-un-fichier-de-git-mais-garder-le-fichier)
- [Je veux rétablir un fichier à une version spécifique](#je-veux-r%C3%A9tablir-un-fichier-%C3%A0-une-version-sp%C3%A9cifique)
- [Je veux lister les changements d'un fichier spécifique entre deux commits ou branches](#je-veux-lister-les-changements-dun-fichier-sp%C3%A9cifique-entre-deux-commits-ou-branches)
- [Je veux que Git ignore les changements d'un fichier spécifique](#je-veux-que-git-ignore-les-changements-dun-fichier-sp%C3%A9cifique)
- [Paramétrage](#param%C3%A9trage)
- [Je veux ajouter des alias pour certaines commandes Git](#je-veux-ajouter-des-alias-pour-certaines-commandes-git)
- [Je veux ajouter un répertoire vide à mon dépôt](#je-veux-ajouter-un-r%C3%A9pertoire-vide-%C3%A0-mon-d%C3%A9p%C3%B4t)
- [Je veux mettre en cache un nom d'utilisateur et mot de passe pour un dépôt](#je-veux-mettre-en-cache-un-nom-dutilisateur-et-mot-de-passe-pour-un-d%C3%A9p%C3%B4t)
- [Je veux que Git ignore les changements de permissions et de filemode](#je-veux-que-git-ignore-les-changements-de-permissions-et-de-filemode)
- [Je veux définir un utilisateur global](#je-veux-d%C3%A9finir-un-utilisateur-global)
- [Je n'ai aucune idée de ce que j'ai mal fait](#je-nai-aucune-id%C3%A9e-de-ce-que-jai-mal-fait)
- [Autres Ressources](#autres-ressources)
- [Livres](#livres)
- [Tutoriels](#tutoriels)
- [Scripts et Outils](#scripts-et-outils)
- [Clients graphiques](#clients-graphiques)
## Dépôts
### Je veux initialiser un dépôt local
Pour initialiser un répertoire existant comme dépôt Git :
```sh
(mon-dossier) $ git init
```
### Je veux cloner un dépôt distant
Pour cloner (copier) un dépôt distant, copiez l'url de ce dernier, et lancez :
```sh
$ git clone [url]
```
Ceci le sauvegardera dans un dossier nommé d'après le nom du dépôt. Assurez-vous d'avoir une connexion avec le serveur distant que vous essayez de cloner (dans la plupart des cas ceci revient à s'assurer d'être connecté·e à Internet).
Pour le cloner vers un dossier ayant un nom différent de celui du dépôt distant :
```sh
$ git clone [url] nom-du-nouveau-dossier
```
## Éditer des Commits
### Qu'est-ce que je viens de commiter ?
Imaginons que vous venez tout juste d'enregistrer des changements à l'aveugle avec {code0}git commit -a{/code0} et que vous n'êtes pas sûr·e du contenu réel du commit que vous venez d'effectuer. Vous pouvez visualiser le dernier commit sur votre HEAD actuel avec :
```sh
(main)$ git show
```
Ou :
```sh
$ git log -n1 -p
```
Si vous voulez voir un fichier à un commit spécifique, vous pouvez aussi faire (où `` est le commit qui vous intéresse).
```sh
$ git show :nomdufichier
```
### J'ai commis une erreur dans un message de commit
Si vous vous êtes trompé·e et que le commit n'a pas encore été poussé, vous pouvez appliquer la commande suivante afin de changer le message du commit sans affecter les changements de ce même commit :
```sh
$ git commit --amend --only
```
Cela ouvrira votre éditeur de texte par défaut, où vous pourrez éditer le message. Vous pouvez également effectuer la même chose en une seule commande :
```sh
$ git commit --amend --only -m 'xxxxxxx'
```
Si vous avez déjà poussé le message, vous pouvez modifier le commit et pousser de force, mais cela n'est pas recommandé.
### J'ai commit avec le mauvais nom et email dans les configurations
Si c'est un commit unique, modifiez-le :
```sh
$ git commit --amend --no-edit --author "Nouvel Auteur·trice "
```
Une alternative est de configurer correctement vos paramètres d'auteur·trice avec `git config --global author.(name|email)` puis de lancer :
```sh
$ git commit --amend --reset-author --no-edit
```
Si vous avez besoin de changer tout l'historique, veuillez vous référer à la page man pour `git filter-branch`.
### Je veux retirer un fichier du commit précédent
Pour retirer les changements effectués sur un fichier lors du précédent commit, faites la chose suivante :
```sh
$ git checkout HEAD^ monfichier
$ git add monfichier
$ git commit --amend --no-edit
```
Dans le cas où le fichier fut introduit dans le commit et que vous voulez le retirer (de Git seul), faites :
```sh
$ git rm --cached monfichier
$ git commit --amend --no-edit
```
Cela est particulièrement utile quand vous avez un patch d'ouvert et que vous avez commité un fichier non nécessaire, et que vous avez besoin de pousser de force pour mettre à jour le patch sur le dépôt distant. L'option `--no-edit` est utilisée pour garder le message de commit existant.
### Je veux supprimer ou retirer mon dernier commit
Si vous avez besoin de supprimer des commits poussés, vous pouvez utiliser la commande suivante. Cependant, cela modifiera votre historique de manière irreversible, et mettra la pagaille dans l'historique de quiconque ayant déjà récupéré des changements depuis le dépôt distant. Pour faire court, si vous n'êtes pas sûr·e, vous ne devriez pas faire ça, jamais.
```sh
$ git reset HEAD^ --hard
$ git push --force-with-lease [remote] [branche]
```
Si vous n'avez pas poussé, pour réinitialiser Git vers l'état dans lequel il était avant que vous ne fassiez votre dernier commit (tout en gardant vos changements) :
```
(ma-branche)$ git reset --soft HEAD^
```
Cela ne marchera que si vous n'avez pas poussé. Si vous avez poussé, la seule vraie chose sécurisée à faire est `git revert SHAduMauvaisCommit`. Cela créera un nouveau commit qui annule tous les changements du commit en question. Ou, si la branche vers laquelle vous avez poussé est "rebase-safe" (en d'autres termes, les autres développeur·euse·s ne la récupéreront pas), vous pouvez juste lancer `git push --force-with-lease`. Pour plus d'informations, jetez un œil [à la section ci-dessus](#je-veux-supprimer-ou-retirer-mon-dernier-commit).
### Supprimer/retirer un commit arbitraire
Le même avertissement que ci-dessus s'applique. Ne faites jamais cela si possible.
```sh
$ git rebase --onto SHA1_DU_MAUVAIS_COMMIT^ SHA1_DU_MAUVAIS_COMMIT
$ git push --force-with-lease [remote] [branche]
```
Ou faites un [rebase interactif](#interactive-rebase) et retirez les lignes correspondantes au(x) commit(s) que vous souhaitez supprimer.
### J'ai essayé de pousser un commit modifié vers le dépôt distant, mais j'ai eu un message d'erreur
```sh
To https://github.com/votrenomdutilisateur/repo.git
! [rejected] ma-branche -> ma-branche (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```
Notez que, tout comme avec un rebase (voir ci-dessous), le fait de modifier **remplace l'ancien commit avec un nouveau**, donc vous devez pousser de force (`--force-with-lease`) vos changements si vous avez déjà poussé le commit vers votre dépôt distant avant sa modification. Soyez prudent·e quand vous faites cela : Assurez vous de *toujours* spécifier une branche !
```sh
(ma-branche)$ git push origin ma-branche --force-with-lease
```
En règle générale, **évitez de pousser de force**. Il est préférable de créer et pousser un nouveau commit plutôt que de pousser de force un commit modifié car cela causera des conflits dans l'historique pour n'importe quel développeur·euse ayant déjà intéragit avec la branche en question ou quelque branche fille. `--force-with-lease` échouera toujours, si quelqu'un d'autre était déjà en train de travailler sur la même branche que vous et que votre push réécrirait par-dessus ces modifications.
Si vous êtes *absolument* sûr·e que personne n'est en train de travailler sur la même branche que vous ou que vous souhaitez mettre à jour la branche de manière *inconditionnelle*, vous pouvez utiliser `--force` (`-f`), mais cela devrait être évité en général.
### J'ai fait un hard reset par accident, et je veux retrouver mes changements
Si vous avez accidentellement fait un `git reset --hard`, vous pouvez normalement retrouver votre commit, car Git garde un log de tout ce que vous faites pendant quelques jours.
À noter : Cela n'est valide que si votre travail est sauvegardé, c'est à dire dans un commit ou un remisage. `git reset --hard` *supprimera* les modifications non commitées, donc utilisez cela avec précaution (une option plus sûre est `git reset --keep`).
```sh
(main)$ git reflog
```
Vous verrez une liste de vos précédents commits, et un commit pour la réinitialisation. Choisissez le SHA du commit vers lequel vous souhaitez retourner, et réinitialisez à nouveau :
```sh
(main)$ git reset --hard SHA1234
```
Et cela devrait faire l'affaire.
### J'ai commité et poussé une fusion par accident
Si vous avez accidentellement fusionné une branche d'une fonctionnalité avec la branche de développement principale avant qu'elle ne soit prête à être fusionnée, vous pouvez toujours annuler cette fusion. Mais il y a un piège : un commit de fusion a plus d'un parent (en général deux).
Utilisez cette commande :
```sh
(feature-branch)$ git revert -m 1
```
où l'option `-m 1` demande de sélectionner le parent numéro 1 (la branche vers laquelle la fusion a été faite) comme parent vers lequel annuler la fusion.
À noter : le numéro du parent n'est pas un identifiant de commit. Un commit de fusion ressemble plus à `Merge: 8e2ce2d 86ac2e7`. Le numéro du parent est l'index basé sur 1 du parent souhaité sur cette ligne, le premier identifiant est le numéro 1, le second le numéro 2, et ainsi de suite.
### J'ai commité et poussé des fichiers contenant des données sensibles par accident
Si vous avez accidentellement poussé des fichiers contenant des données sensibles (mots de passe, clés, etc.), vous pouvez modifier le commit précédent. Gardez toutefois à l'esprit qu'une fois que vous avez poussé un commit, vous devez considérer n'importe quelle donnée qu'il contient comme étant compromise. Ces étapes peuvent supprimer les données sensibles de votre dépôt public ou de votre copie locale, mais vous ne **pouvez pas** supprimer les données sensibles des copies clonées par d'autres personnes. Si vous avez commité un mot de passe, **changez-le immédiatement**. Si vous avez commité une clé, **révoquez-la et régénérez-la immédiatement**. Modifier le commit poussé n'est pas suffisant, étant donné que n'importe qui aurait pu extraire le commit original contenant vos données sensibles pendant ce temps.
Si vous souhaitez modifier le fichier et supprimer les données sensibles, lancez :
```sh
(feature-branch)$ git add fichier_modifié
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branche]
```
Si vous souhaitez supprimer tout un fichier (mais le garder en local), lancez :
```sh
(feature-branch)$ git rm --cached fichier_sensible
echo fichier_sensible >> .gitignore
(feature-branch)$ git add .gitignore
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branche]
```
Alternativement, stockez vos données sensibles dans des variables d'environnement locales.
Si vous souhaitez supprimer tout un fichier (et ne pas le garder en local), lancez :
```sh
(feature-branch)$ git rm fichier_sensible
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branche]
```
Si vous avez créé d'autres commits pendant ce temps (c'est à dire que les données sensibles sont dans un commit avant le commit précédent), vous devrez utiliser la fonction `rebase`.
## Indexation
### J'ai besoin d'ajouter des modifications indexées sur le commit précédent
```sh
(ma-branche*)$ git commit --amend
```
Si vous savez déjà que vous ne souhaitez pas modifier le message du commit, vous pouvez dire à Git de réutiliser ce dernier :
```sh
(ma-branche*)$ git commit --amend -C HEAD
```
### Je veux indexer une partie d'un fichier, mais pas tout le fichier
Normalement, quand vous souhaitez modifier une partie d'un fichier, il faut lancer ceci :
```sh
$ git add --patch nomdufichier.x
```
`-p` fonctionnera comme raccourci. Cela ouvrira le mode interactif. Vous serez en mesure d'utiliser l'option `s` pour séparer le commit. Cependant, si le fichier est nouveau, vous n'aurez pas cette option. Pour ajouter un nouveau fichier, faites comme ceci :
```sh
$ git add -N nomdufichier.x
```
Ensuite, vous devrez utiliser l'option `e` afin de choisir manuellement quelles lignes ajouter. Lancer `git diff --cached` ou `git diff --staged` vous montrera quelles lignes vous avez indexées comparées à celles qui sont toujours sauvegardées en local.
### Je veux ajouter les changements d'un fichier dans deux commits différents
`git add` ajoutera le fichier entier à un commit. `git add -p` vous permettra de sélectionner interactivement quels changements vous souhaitez ajouter.
### Je veux indexer mes modifications indexées, et désindexer mes modifications indexées
Cela est délicat. La meilleure chose que nous pouvons vous conseiller est que vous devriez remiser vos modifications non indexées, puis utiliser `git reset`. Après cela, utilisez `pop` pour déremiser vos modifications, puis ajoutez-les :
```sh
$ git stash -k
$ git reset --hard
$ git stash pop
$ git add -A
```
## Modifications non indexées
### Je veux déplacer mes modifications non indexées vers une nouvelle branche
```sh
$ git checkout -b ma-branche
```
### Je veux déplacer mes modifications non indexées vers une branche différente existante
```sh
$ git stash
$ git checkout ma-branche
$ git stash pop
```
### Je veux me débarrasser de mes modifications locales non commitées (indexées et non-indexées)
Si vous voulez vous débarrasser de toutes vos modifications locales indexées et non-indexées, vous pouvez faire ceci :
```sh
(ma-branche)$ git reset --hard
# ou
(main)$ git checkout -f
```
Cette commande désindexera tous les fichiers que vous avez pu indexer avec `git add` :
```sh
$ git reset
```
Cette commande annulera toutes les modifications locales non commitées (elle devrait être exécutée à la racine du dépôt) :
```sh
$ git checkout .
```
Vous pouvez également annuler les changements non commités d'un fichier ou d'un dossier en particulier :
```sh
$ git checkout [un_dossier|fichier.txt]
```
Il existe également une autre façon d'annuler toutes vos modifications non commitées (plus longue à taper, mais fonctionne depuis n'importe quel sous-dossier) :
```sh
$ git reset --hard HEAD
```
Cette commande supprimera tous les fichiers locaux non indexés, afin que seuls les fichiers suivis par Git ne restent :
```sh
$ git clean -fd
```
`-x` supprimera également tous les fichiers ignorés.
### Je veux me débarrasser de modifications non-indexées spécifiques
Quand vous souhaitez vous débarrasser de quelques modifications dans votre copie de travail, mais pas toutes, faites un `checkout` sur les modifications non désirées, et gardez les bonnes :
```sh
$ git checkout -p
# Répondez y à tous les bouts de code que vous souhaitez jeter
```
Une autre stratégie implique d'utiliser `stash`. Remisez toutes les bonnes modifications, réinitialisez votre copie locale, et réappliquez les bonnes modifications :
```sh
$ git stash -p
# Sélectionnez tous les bouts de code que vous souhaitez garder
$ git reset --hard
$ git stash pop
```
Alternativement, remisez vos changements non désirés, puis jetez le stash :
```sh
$ git stash -p
# Sélectionnez tous les bouts de code que vous ne souhaitez pas sauvegarder
$ git stash drop
```
### Je veux me débarrasser de fichiers non-indexés spécifiques
Quand vous souhaitez vous débarrasser d'un fichier en particulier de votre copie de travail :
```sh
$ git checkout monFichier
```
Alternativement, pour vous débarrasser de plusieurs fichiers dans votre copie locale, listez chacun d'entre eux :
```sh
$ git checkout monPremierFichier monDeuxiemeFichier
```
### Je veux me débarrasser de mes modifications locales non-indexées uniquement
Quand vous souhaitez vous débarrasser de toutes vos modifications locales non commitées :
```sh
$ git checkout .
```
### Je veux me débarrasser de tous mes fichiers non suivis
Quand vous souhaitez vous débarrasser de tous vos fichiers non suivis :
```sh
$ git clean -f
```
### Je veux désindexer un fichier indexé spécifique
Il arrive parfois que nous ayons un ou plusieurs fichiers qui ont été indexés par accident. Et ces fichiers n'ont pas été commités auparavant. Pour les désindexer :
```sh
$ git reset --
```
Cela entraînera la désindexation du fichier et le remettra à son état non suivi.
## Branches
### Je veux lister toutes les branches
Pour lister les branches locales :
```sh
$ git branch
```
Pour lister les branches distantes :
```sh
$ git branch -r
```
Pour lister toutes les branches (à la fois locales et distantes) :
```sh
$ git branch -a
```
### Créer une branche à partir d'un commit
```sh
$ git checkout -b
```
### J'ai pull depuis/vers la mauvaise branche
C'est une autre occasion d'utiliser `git reflog` afin de voir où votre HEAD pointait avant le mauvais `pull` :
```sh
(main)$ git reflog
ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward
c5bc55a HEAD@{1}: checkout: checkout message goes here
```
Réinitialisez simplement votre branche vers le commit désiré :
```sh
$ git reset --hard c5bc55a
```
Et voilà.
### Je veux supprimer mes commits locaux afin que ma branche soit pareille à celle sur le serveur
Assurez-vous que vous n'avez pas poussé vos modifications sur le serveur.
`git status` devrait vous indiquer combien de commits en avance vous êtes par rapport à origin :
```sh
(ma-branche)$ git status
# On branch ma-branche
# Your branch is ahead of 'origin/my-branch' by 2 commits.
# (use "git push" to publish your local commits)
#
```
Une des façons de faire pour réinitialiser votre branche afin qu'elle corresponde à origin (afin d'avoir la même chose que le dépôt distant) est de lancer ceci :
```sh
(ma-branche)$ git reset --hard origin/ma-branche
```
### J'ai commité sur main au lieu d'une nouvelle branche
Créez la nouvelle branche tout en restant sur main :
```sh
(main)$ git branch ma-branche
```
Réinitialisez la branche main vers le commit précédent :
```sh
(main)$ git reset --hard HEAD^
```
`HEAD^` est un raccourci pour `HEAD^1`. Cela indique le premier parent de `HEAD`, de la même façon que `HEAD^2` indique le second parent du commit (des fusions peuvent avoir deux parents).
Notez que `HEAD^2` ne signifie **pas** la même chose que `HEAD~2` (visitez [ce lien](http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde) pour plus d'informations).
Alternativement, si vous ne souhaitez pas utiliser `HEAD^`, retrouvez le hash du commit vers lequel vous souhaitez remettre votre branche main (`git log` devrait faire l'affaire). Puis réinitialisez vers ce hash. `git push` s'assurera que la modification est reflétée sur le serveur distant.
Par exemple, si le hash du commit sur lequel votre branche main est supposée être est `a13b85e` :
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
Déplacez-vous vers la nouvelle branche pour continuer de travailler :
```sh
(main)$ git checkout ma-branche
```
### Je veux séparer tout un fichier d'un autre ref-ish
Admettons que vous avez un pic de travail (voir la note), avec des centaines de changements. Tout fonctionne bien. Maintenant, vous commitez vers une autre branche pour sauvegarder ce travail :
```sh
(solution)$ git add -A && git commit -m "Ajout de tous les changements de ce pic vers un gros commit."
```
Quand vous souhaitez mettre tout cela vers une branche (d'une fonctionnalité ou `develop`), ce qui vous intéresse est de garder les fichiers dans leur entièreté. Vous voudriez également séparer votre gros commit en plusieurs petits commits.
Admettons que vous ayez :
* La branche `solution`, avec la solution à votre pic. En avance d'un commit par rapport à `develop`.
* La branche `develop`, où vous souhaitez ajouter vos modifications.
Vous pouvez résoudre cela en ramenant les contenus vers votre branche :
```sh
(develop)$ git checkout solution -- fichier1.txt
```
Cela ramènera le contenu de ce fichier dans la branche `solution` vers votre branche `develop` :
```sh
# On branch develop
# Your branch is up-to-date with 'origin/develop'.
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: fichier1.txt
```
Puis, commitez comme d'habitude.
Note: Les pics de solutions servent à analyser ou résoudre un problème. Ces solutions sont ensuite utilisées afin d'avoir une estimation puis jetées une fois que tout le monde a une vision claire du problème. ~ [Wikipedia](https://en.wikipedia.org/wiki/Extreme_programming_practices).
### J'ai fait plusieurs commits sur une même branche qui auraient dû être sur plusieurs branches
Admettons que vous êtes sur votre branche main. En lançant `git log`, vous remarquez que vous avez fait deux commits :
```sh
(main)$ git log
commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee
Date: Tue Jul 22 15:39:27 2014 -0400
Bug #21 - Ajout de la protection CSRF
commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee
Date: Tue Jul 22 15:39:12 2014 -0400
Bug #14 - Correction de l'espacement du titre
commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose
Date: Tue Jul 21 01:12:48 2014 -0400
Premier commit
```
Notons dans un coin les hashes des commits pour chaque bug (`e3851e8` pour le #21, `5ea5173` pour le #14).
Pour commencer, réinitialisons notre branche main sur le bon commit (`a13b85e`) :
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
Maintenant, nous pouvons créer une nouvelle branche pour le bug #21 :
```sh
(main)$ git checkout -b 21
(21)$
```
Maintenant, faisons un `cherry-pick` du commit pour le bug #21 par-dessus notre branche. Cela signifie que nous appliquerons ce commit, et seulement ce commit, directement au-dessus de ce qui se trouve sur notre HEAD :
```sh
(21)$ git cherry-pick e3851e8
```
Lors de cette étape, il est possible qu'il y ait des conflits. Regardez la section [**Il y a eu des conflits**](#merge-conflict) dans la section [faire un rebase interactif](#interactive-rebase) ci-dessus pour savoir comment résoudre ces conflits.
Maintenant, créons une nouvelle branche pour le bug #14, aussi basée sur main :
```sh
(21)$ git checkout main
(main)$ git checkout -b 14
(14)$
```
Et pour finir, faisons un `cherry-pick` du commit pour le bug #14 :
```sh
(14)$ git cherry-pick 5ea5173
```
### Je veux supprimer mes branches locales qui ont été supprimée sur le dépôt distant
Une fois que vous avez accepté une Pull Request sur GitHub, vous avez l'option de supprimer la branche fusionnée dans votre fork. Si vous ne prévoyez pas de continuer à travailler sur la branche, cela est plus propre de supprimer les copies locales de la branche afin de ne pas encombrer votre copie de travail avec un tas de branches obsolètes.
```sh
$ git fetch -p upstream
```
où `upstream` est le dépôt distant depuis lequel vous voulez mettre à jour.
### J'ai supprimé ma branche par accident
Si vous poussez régulièrement sur la branche distante, vous devriez ne pas avoir de problème la plupart du temps. Mais il arrive parfois que vous finissez par supprimer vos branches. Admettons que nous créons une nouvelle branche avec un nouveau fichier :
```sh
(main)$ git checkout -b ma-branche
(ma-branceh)$ git branch
(ma-branche)$ touch foo.txt
(ma-branche)$ ls
README.md foo.txt
```
Ajoutons cela et commitons :
```sh
(ma-branche)$ git add .
(ma-branche)$ git commit -m 'ajout de foo.txt'
(ma-branche)$ ajout de foo.txt
1 files changed, 1 insertions(+)
create mode 100644 foo.txt
(ma-branche)$ git log
commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
Author: siemiatj
Date: Wed Jul 30 00:34:10 2014 +0200
ajout de foo.txt
commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson
Date: Tue Jul 29 13:14:46 2014 -0400
Correction #6: Push de force après avoir édité les commits
```
Maintenant, revenons sur main et supprimons notre branche "par accident" :
```sh
(ma-branche)$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
(main)$ git branch -D my-branch
Deleted branch ma-branche (was 4e3cd85).
(main)$ echo oh non, j'ai supprimé ma branche !
oh non, j'ai supprimé ma branche !
```
À cette étape, vous devriez devenir familier avec `reflog`, un logueur amélioré. Il stocke l'historique de toutes les actions dans le dépôt :
```
(main)$ git reflog
69204cd HEAD@{0}: checkout: moving from ma-branche to main
4e3cd85 HEAD@{1}: commit: ajout de foo.txt
69204cd HEAD@{2}: checkout: moving from main to ma-branche
```
Comme vous pouvez le remarquer, nous avons le hash du commit de notre branche supprimée. Voyons voir si nous pouvons restaurer notre branche supprimée.
```sh
(main)$ git checkout -b ma-branche-help
Switched to a new branch 'ma-branche-help'
(ma-branche-help)$ git reset --hard 4e3cd85
HEAD is now at 4e3cd85 ajout de foo.txt
(ma-branche-help)$ ls
README.md foo.txt
```
Voilà ! Nous avons récupéré notre fichier supprimé. `git reflog` est aussi utile quand un rebase se passe terriblement mal.
### Je veux supprimer une branche
Pour supprimer une branche distante :
```sh
(main)$ git push origin --delete ma-branche
```
Vous pouvez aussi faire :
```sh
(main)$ git push origin :ma-branche
```
Pour supprimer une branche locale :
```sh
(main)$ git branch -d ma-branche
```
Pour supprimer une branche locale qui *n'a pas* été fusionnée vers la branche actuelle ou une branche distante :
```sh
(main)$ git branch -D ma-branche
```
### Je veux supprimer plusieurs branches
Admettons que vous voulez supprimer toutes les branches qui commencent par `fix/` :
```sh
(main)$ git branch | grep 'fix/' | xargs git branch -d
```
### Je veux renommer une branche
Je veux renommer la branche actuelle (locale) :
```sh
(main)$ git branch -m nouveau-nom
```
Pour renommer une autre branche (locale) :
```sh
(main)$ git branch -m ancien-nom nouveau-nom
```
### Je veux me déplacer sur une branche distante sur laquelle quelqu'un est en train de travailler
Pour commencer, récupérez toutes les branches depuis le dépôt distant :
```sh
(main)$ git fetch --all
```
Admettons que vous souhaitez vous déplacer sur `daves` depuis le dépôt distant :
```sh
(main)$ git checkout --track origin/daves
Branch daves set up to track remote branch daves from origin.
Switched to a new branch 'daves'
```
(`--track` est un raccourci pour `git checkout -b [branch] [remotename]/[branch]`)
Cela vous donnera une copie locale de la branche `daves`, et n'importe quelle mise à jour qui y sera poussée sera aussi visible sur le dépôt distant.
### Je veux créer une nouvelle branche distante à partir de celle en locale
```sh
$ git push HEAD
```
Si vous voulez configurer cette branche distante en tant qu'upstream pour la branche actuelle, lancez la commande suivante à la place :
```sh
$ git push -u HEAD
```
Avec le mode `upstream` et le mode `simple` (défaut dans Git 2.0) de la configuration de `push.default`, la commande suivante poussera la branche actuelle par rapport à la branche distante qui a été précédemment enregistrée avec `-u` :
```sh
$ git push
```
Le comportement des autres modes de `git push` est détaillé dans la [documentation de `push.default`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-pushdefault).
### Je veux configurer une branche distante en tant qu'upstream pour une branche locale
Vous pouvez configurer une branche distante en tant qu'upstream pour la branche locale actuelle en utilisant :
```sh
$ git branch --set-upstream-to [nomduremote]/[branche]
# ou, en utilisant le raccourci :
$ git branch -u [nomduremote]/[branche]
```
Pour configurer la branche distante en tant qu'upstream pour une autre branche locale :
```sh
$ git branch -u [nomduremote]/[branche] [branche-locale]
```
### Je veux configurer mon HEAD pour suivre la branche distante par défaut
En vérifiant vos branches distantes, vous pouvez voir lesquelles d'entre-elles sont suivies par HEAD. Dans certains cas, ce n'est pas la branche désirée.
```sh
$ git branch -r
origin/HEAD -> origin/gh-pages
origin/main
```
Pour changer cela afin que `origin/HEAD` suive `origin/main`, vous pouvez lancer cette commande :
```sh
$ git remote set-head origin --auto
origin/HEAD set to main
```
### J'ai fait des modifications sur la mauvaise branche
Si vous avez fait des modifications non commitées et réalisez par la suite que vous êtes sur la mauvaise branche, remisez les modifications et appliquez-les sur la branche que vous voulez :
```sh
(mauvaise_branche)$ git stash
(mauvaise_branche)$ git checkout
(bonne_branche)$ git stash apply
```
## Rebaser et fusionner
### Je veux annuler un rebase/merge
Il se peut que vous avez fait une fusion ou un rebase sur votre branche actuelle avec une mauvaise branche, ou que vous vous êtes perdus ou n'arrivez pas à finir le processus de rebase ou de fusion. Git sauvegarde le pointeur de HEAD original dans une variable appelée ORIG_HEAD avant de faire des opérations dangereuses, afin qu'il soit plus facile de réinitialiser votre branche dans l'état avant le rebase ou la fusion :
```sh
(ma-branche)$ git reset --hard ORIG_HEAD
```
### J'ai rebase, mais je ne veux pas pousser de force
Malheureusement, vous devez pousser de force si vous souhaitez que ces modifications soient répercutées sur la branche distante. C'est parce que vous avez changé l'historique. La branche distante n'acceptera pas les modifications, sauf si vous poussez de force. C'est l'une des principales raisons pour lesquelles de nombreuses personnes utilisent un flux de travail à base de fusions plutôt qu'un flux de travail à base de rebase : les grandes équipes peuvent avoir des problèmes avec les développeurs qui poussent de force. Utilisez ceci avec prudence. Une façon plus sûre d'utiliser rebase est de ne pas du tout refléter vos modifications sur la branche distante, mais plutôt de procéder de la manière suivante :
```sh
(main)$ git checkout ma-branche
(ma-branche)$ git rebase -i main
(ma-branche)$ git checkout main
(main)$ git merge --ff-only ma-branche
```
Pour plus d'informations, visitez [ce thread de SO](https://stackoverflow.com/questions/11058312/how-can-i-use-git-rebase-without-requiring-a-forced-push).
### J'ai besoin de combiner des commits
Admettons que vous êtes en train de travailler sur une branche qui sera ou est dans une demande de fusion vers `main`. Dans le cas le plus simple, il suffit de combiner *tous* vos commits en un seul et vous vous fichez des timestamps des commits, vous pouvez faire un reset et recommiter. Assurez-vous que la branche main est à jour et que toutes vos modifications sont commitées, puis faites :
```sh
(ma-branche)$ git reset --soft main
(ma-branche)$ git commit -am "Nouvelle fonctionnalité géniale"
```
Si vous voulez plus de contrôle, et également conserver les timestamps, vous devez faire ce qu'on appelle un rebase interactif :
```sh
(ma-branche)$ git rebase -i main
```
Si vous ne travaillez pas par rapport à une autre branche, vous allez devoir `rebase` relativement à votre `HEAD`. Si vous voulez combiner les 2 derniers commits par exemple, vous allez devoir `rebase` par rapport à `HEAD~2`. Pour les 3 derniers, `HEAD~3`, etc.
```sh
(main)$ git rebase -i HEAD~2
```
Après avoir lancé votre commande `rebase`, vous verrez quelque chose dans le genre dans votre éditeur de texte :
```vim
pick a9c8a1d Un peu de refactor
pick 01b2fd8 Nouvelle feature génial
pick b729ad5 fixup
pick e3851e8 autre fix
# Rebase 8074d12..b729ad5 onto 8074d12
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```
Toutes les lignes commençant par un `#` sont des commentaires, elles n'affecteront pas votre rebase.
Vous devez ensuite remplacer les commandes `pick` avec n'importe laquelle parmi celles présentes dans la liste ci-dessus, et vous pouvez également supprimer des commits en supprimant les lignes correspondantes.
Par exemple, si vous voulez **laisser le commit le plus vieux (le premier) tel quel et combiner tous les commits suivants avec le deuxième plus vieux**, vous devez remplacer les lettres à côté de chaque commit sauf le premier et le deuxième par un `f` :
```vim
pick a9c8a1d Un peu de refactor
pick 01b2fd8 Nouvelle feature géniale
f b729ad5 fixup
f e3851e8 autre fix
```
Si vous voulez combiner ces commits **et renommer le commit**, il vous faudra ajouter un `r` à côté du deuxième commit ou utiliser simplement `s` au lieu de `f` :
```vim
pick a9c8a1d Un peu de refactor
pick 01b2fd8 Nouvelle feature géniale
s b729ad5 fixup
s e3851e8 autre fix
```
Vous pouvez ensuite renommer le commit dans le prochain éditeur de texte qui s'affiche :
```vim
Nouvelles fonctionnalités encore plus géniales
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 8074d12
# You are currently editing a commit while rebasing branch 'main' on '8074d12'.
#
# Changes to be committed:
# modified: README.md
#
```
Si tout se passe bien, vous devriez voir quelque chose comme ceci :
```sh
(main)$ Successfully rebased and updated refs/heads/main.
```
#### Stratégie de fusion sûre
`--no-commit` fait une fusion mais prétend que la fusion a échoué et ne commit pas automatiquement, laissant la chance à l'utilisateur d'inspecter plus et de bidouiller plus les résultats de la fusion avant de commiter. `no-ff` garde une preuve qu'une branche de fonctionnalité a auparavant existé, gardant ainsi l'historique du projet consistant :
```sh
(main)$ git merge --no-ff --no-commit ma-branche
```
#### J'ai besoin de fusionner deux branches en un seul commit
```sh
(main)$ git merge --squash ma-branche
```
#### Je veux combiner les commits non poussés uniquement
Parfois, vous avez plusieurs commits en cours que vous souhaitez combiner avant de les pousser sur l'upstream. Vous ne voulez pas combiner des commits qui ont déjà été poussés sur l'upstream par accident, car quelqu'un d'autre a peut-être déjà effectué des commits qui y font référence :
```sh
(main)$ git rebase -i @{u}
```
Cela fera un rebase interactif que liste seulement les commits que vous n'avez pas poussé, afin que cela soit plus sûr de réordonner/corriger/combiner n'importe lesquels de la liste.
#### J'ai besoin d'annuler la fusion
Parfois, une fusion peux produire des problèmes pour certains fichiers. Dans ces cas là, nous pouvons utiliser l'option `abort` afin d'abonner le processus de résolution de conflits en cours, afin d'essayer de reconstruire l'état qu'on avait avant la fusion :
```sh
(ma-branche)$ git merge --abort
```
Cette commande est disponible dans Git depuis les versions >= 1.7.4
### J'ai besoin de mettre à jour le commit parent de ma branche
Admettons que vous avez une branche main, une branche feature-1 créée à partir de main, et une branche feature-2 créée à partir de feature-1, puis que le commit parent de feature-2 n'est plus le bon (il devrait être celui correspondant au HEAD de feature-1, étant donné que notre branche a été créée à partir de celui-ci). Vous pouvez réparer ça avec `git rebase --onto` :
```sh
(feature-2)$ git rebase --onto feature-1 feature-2
```
Cela peut vous venir en aide dans les situations délicates où vous avez une fonctionnalité créée sur la base d'une autre fonctionnalité qui n'a pas encore été fusionnée, et qu'une correction de bug sur la branche feature-1 a besoin d'être reflétée sur votre branche feature-2.
### Vérifier si tous les commits d'une branche sont fusionnés
Pour vérifier si tous les commits d'une branche sont fusionnés sur une autre branche, vous devriez comparer les HEAD (ou n'importe quel autre commit) de ces branches :
```sh
(main)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
```
Cela vous dira si des commits sont dans l'une mais pas dans l'autre, et vous donnera une liste de tout ce qui n'est pas commun aux deux branches. Une alternative est de faire ceci :
```sh
(main)$ git log main ^feature/120-on-scroll --no-merges
```
### Problèmes possibles avec les rebase interactifs
#### L'écran d'édition du rebase affiche 'noop'
Si vous voyez cela :
```
noop
```
Cela signifie que vous êtes en train de rebaser par rapport à une branche qui a un commit identique, ou qui est *en avance* par rapport à votre branche actuelle. Vous pouvez essayer :
* de vous assurez que votre branche main est là où elle devrait être
* de rebaser par rapport à `HEAD~2` ou plus tôt à la place
#### Il y a eu des conflits
Si vous n'êtes pas en mesure de terminer le rebase avec succès, vous allez peut-être devoir résoudre des conflits.
Pour commencer, lancez `git status` afin de voir quels fichiers ont des conflits :
```sh
(ma-branche)$ git status
On branch ma-branche
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
both modified: README.md
```
Dans cet exemple, `README.md` a des conflits. Ouvrez le fichier et cherchez la chose suivante :
```vim
<<<<<<< HEAD
some code
=========
some code
>>>>>>> nouveau-commit
```
Vous aurez besoin de résoudre les différences entre le code qui fut ajouté dans votre nouveau commit (dans cet exemple, tout ce qu'il y a entre la ligne du milieu et `nouveau-commit`) et votre `HEAD`.
Si vous voulez garder la version du code d'une des branches, vous pouvez utiliser `--ours` ou `--theirs` :
```sh
(main*)$ git checkout --ours README.md
```
- Quand vous *fusionnez*, utilisez `--ours` pour garder les modifications de la branche locale, ou `--theirs` pour garder les modifications de l'autre branche.
- Quand vous *rebasez*, utilisez `--theirs` pour garder les modifications de la branche locale, ou `--ours` pour garder les modifications de l'autre branche. Pour des explications concernant cet échange, consultez [cette note dans la documentation de Git](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---merge).
Si les fusions sont plus complexes, vous pouvez utiliser un éditeur de diff visuel :
```sh
(main*)$ git mergetool -t opendiff
```
Après avoir résolu tous les conflits et testé votre code, faite un `git add` sur les fichiers que vous avez modifiés, puis continuez le rebase avec `git rebase --continue` :
```sh
(ma-branche)$ git add README.md
(ma-branche)$ git rebase --continue
```
Si après avoir résolu tous les conflits, vous finissez avec un arbre identique à ce qu'il était avant le commit, vous devriez faire un `git rebase --skip` à la place.
Si à n'importe quel moment vous voulez arrêter tout le rebase et revenir à l'état initial de votre branche, vous pouvez faire ceci :
```sh
(ma-branche)$ git rebase --abort
```
## Remisage
### Remiser toutes les modifications
Pour remiser toutes les modifications dans votre dossier de travail :
```sh
$ git stash
```
Si vous voulez également remiser les fichiers non suivis, utilisez l'option `-u` :
```sh
$ git stash -u
```
### Remiser des fichiers spécifiques
Pour remiser les modifications dans votre dossier de travail uniquement :
```sh
$ git stash push chemin-du-dossier-de-travail/nomdufichier.ext
```
Pour remiser plusieurs fichiers de votre dossier de travail :
```sh
$ git stash push chemin-du-dossier-de-travail/nomdufichier1.ext chemin-du-dossier-de-travail/nomdufichier2.ext
```
### Remiser avec un message
```sh
$ git stash save
```
### Appliquer un remisage spécifique de la liste
Commencez par vérifier la liste de vos remisages avec un message en utilisant :
```sh
$ git stash list
```
Puis appliquez la commande suivante en choisissant un remisage de la liste :
```sh
$ git stash apply "stash@{n}"
```
Ici, `n` indique la position du remisage dans la pile. Celui le plus haut sera en position 0.
## Rechercher
### Je veux rechercher une chaîne de caractères dans un commit
Pour rechercher une certaine chaîne de caractères dans un commit, vous pouvez utiliser la commande suivante :
```sh
$ git log -S "chaîne de caractères à rechercher"
```
Paramètres communs :
* `--source` sert à montrer le nom du ref en se basant sur la ligne de commande avec laquelle chaque commit a été atteint.
* `--all` sert à commencer depuis chaque branche.
* `--reverse` retourne les résultats dans l'ordre inverse, c'est à dire que la commande affichera le premier commit qui a fait la modification.
### Je veux rechercher par auteur·trice/validateur·trice
Pour rechercher des commits par auteur·trice/validateur·trice, vous pouvez utiliser :
```sh
$ git log --author=
$ git log --committer=
```
Gardez en tête que l'auteur·trice et la personne qui a créé le commit ne sont pas les mêmes personnes. L'option `--author` correspond à la personne qui a écrit le code, et, d'un autre côté, l'option `--committer`, correspond à la personne qui a commité le code au nom de l'autreur·trice.
### Je veux lister les commits ayant des fichiers spécifiques
Pour trouver tous les commits contenant un fichier spécifique, vous pouvez utiliser :
```sh
$ git log --
```
En général, vous préciseriez un chemin exact, mais vous pouvez aussi utiliser des wildcards dans le chemin et le nom du fichier :
```sh
$ git log -- **/*.js
```
En utilisant des wildcards, il est utile de préciser `--name-status` pour voir la liste des fichiers commités :
```sh
$ git log --name-status -- **/*.js
```
### Trouver un tag où un commit est référencé
Pour trouver tous les tags contenant un commit en particulier :
```sh
$ git tag --contains
```
## Sous-modules
### Cloner tous les sous-modules
```sh
$ git clone --recursive git://github.com/foo/bar.git
```
Si ces derniers ont déjà été clonés :
```sh
$ git submodule update --init --recursive
```
### Retirer un sous-module
Créer un sous-module est assez trivial, mais le supprimer l'est moins. Les commandes dont vous avez besoin sont :
```sh
$ git submodule deinit nomdusubmodule
$ git rm nomdusubmodule
$ git rm --cached nomdusubmodule
$ rm -rf .git/modules/nomdusubmodule
```
## Objets divers
### Récupérer un fichier supprimé
Trouvez le commit le plus récent où le fichier existait encore :
```sh
$ git rev-list -n 1 HEAD -- nomdufichier
```
Puis faites un `checkout` sur ce fichier :
```
git checkout idducommitquiasupprimé^ -- nomdufichier
```
### Supprimer un tag
```sh
$ git tag -d
$ git push :refs/tags/
```
### Récupérer un tag supprimé
Si vous voulez récupérer un tag qui a déjà été supprimé, vous pouvez le faire en suivant ces étapes :
Tout d'abord, vous devez retrouver le tag inaccessible en question :
```sh
$ git fsck --unreachable | grep tag
```
Notez le hash du tag dans un coin. Puis, restaurez le tag supprimé avec la commande suivante, qui utilise [`git update-ref`](https://git-scm.com/docs/git-update-ref) :
```sh
$ git update-ref refs/tags/
```
Votre tag devrait maintenant être restauré.
### Patch supprimé
Si quelqu'un vous a envoyé une Pull Request sur GitHub, mais a par la suite supprimé son fork originel, vous ne serez pas en mesure de cloner son dépôt ou d'utiliser `git am` étant donné que les URLs de [.diff, .patch](https://github.com/blog/967-github-secrets) sont devenues indisponibles. Mais vous pouvez vous déplacer sur la PR en elle-même en utilisant les [les refs spéciaux de GitHub](https://gist.github.com/piscisaureus/3342247). Pour récupérer le contenu de PR#1 vers une nouvelle branche intitulée pr_1 :
```sh
$ git fetch origin refs/pull/1/head:pr_1
From github.com:foo/bar
* [new ref] refs/pull/1/head -> pr_1
```
### Exporter un dépôt comme fichier Zip
```sh
$ git archive --format zip --output /full/path/to/zipfile.zip main
```
### Pousser une branche et un tag qui ont le même nom
Si un tag sur le dépôt distant a le même nom qu'une branche, vous aurez le message d'erreur suivant en essayant de pousser cette branche avec la commande standard `$ git push ` :
```sh
$ git push origin
error: dst refspec same matches more than one.
error: failed to push some refs to ''
```
Réparez cela en spécifiant que vous voulez pousser la référence de HEAD :
```sh
$ git push origin refs/heads/
```
Si vous voulez pousser un tag sur un dépôt distant qui a le même nom qu'une branche, vous pouvez utiliser une commande similaire :
```sh
$ git push origin refs/tags/
```
## Suivre des fichiers
### Je veux changer la capitalisation du nom d'un fichier, sans changer son contenu
```sh
(main)$ git mv --force monFichier MonFichier
```
### Je veux écraser des fichiers locaux en faisant un git pull
```sh
(main)$ git fetch --all
(main)$ git reset --hard origin/main
```
### Je veux retirer un fichier de Git mais garder le fichier
```sh
(main)$ git rm --cached log.txt
```
### Je veux rétablir un fichier à une version spécifique
Supposons que le hash du commit que vous voulez est `c5f567` :
```sh
(main)$ git checkout c5f567 -- file1/to/restore file2/to/restore
```
Si vous voulez rétablir les changements effectués un commit avant `c5f567`, passez le hash du commit comme étant `c5f567~1` :
```sh
(main)$ git checkout c5f567~1 -- file1/to/restore file2/to/restore
```
### Je veux lister les changements d'un fichier spécifique entre deux commits ou branches
Supposons que vous voulez comparer le dernier commit avec le fichier du commit `c5f567` :
```sh
$ git diff HEAD:path_to_file/file c5f567:path_to_file/file
# ou
$ git diff HEAD c5f567 -- path_to_file/file
```
Il en est de même pour les branches :
```sh
$ git diff main:path_to_file/file staging:path_to_file/file
# ou
$ git diff main staging -- path_to_file/file
```
### Je veux que Git ignore les changements d'un fichier spécifique
Cela marche très bien pour le modèles de configurations ou d'autres fichier qui nécessitent d'ajouter des identifiants qui ne devraient pas être commités :
```sh
$ git update-index --assume-unchanged file-to-ignore
```
Notez que cela n'enlève *pas* le fichier du suivi - il est seulement ignoré localement. Pour annuler cela et dire à Git de suivre les modifications à nouveau, cette commande supprime le fait d'ignorer :
```sh
$ git update-index --no-assume-unchanged fichier-à-ne-plus-ignorer
```
## Paramétrage
### Je veux ajouter des alias pour certaines commandes Git
Sur macOS et Linux, votre fichier de configuration Git est stocké dans `~/.gitconfig`. J'ai ajouté des exemples d'alias que j'utilise comme raccourcis (et certaines de mes fautes de frappes) dans la section `[alias]` comme ci-dessous :
```vim
[alias]
a = add
amend = commit --amend
c = commit
ca = commit --amend
ci = commit -a
co = checkout
d = diff
dc = diff --changed
ds = diff --staged
extend = commit --amend -C HEAD
f = fetch
loll = log --graph --decorate --pretty=oneline --abbrev-commit
m = merge
one = log --pretty=oneline
outstanding = rebase -i @{u}
reword = commit --amend --only
s = status
unpushed = log @{u}
wc = whatchanged
wip = rebase -i @{u}
zap = fetch -p
```
### Je veux ajouter un répertoire vide à mon dépôt
Vous ne pouvez pas ! Git ne supporte pas cela, mais il y a une astuce. Vous pouvez créer un fichier `.gitignore` dans le dossier avec le contenu suivant :
```
# Ignorer tout dans ce dossier
*
# Sauf ce fichier
!.gitignore
```
Une autre convention commune est de créer un fichier vide dans le dossier, intitulé `.gitkeep` :
```sh
$ mkdir mondossier
$ touch mondossier/.gitkeep
```
Vous pouvez aussi juste appeler le fichier `.keep`, dans ce cas la commande ci-dessus serait `touch mydir/.keep`.
### Je veux mettre en cache un nom d'utilisateur et mot de passe pour un dépôt
Il se peut que vous ayez un dépôt qui nécessite une authentification. Dans ce cas, vous pouvez mettre en cache un nom d'utilisateur et un mot de passe afin que vous n'ayez pas à les rentrer à chaque push / pull. Cette commande peut faire cela pour vous :
```sh
$ git config --global credential.helper cache
# Configure Git pour utiliser le cache mémoire des identifiants
```
```sh
$ git config --global credential.helper 'cache --timeout=3600'
# Configurer le cache pour expirer après une heure (le paramètre est en secondes)
```
### Je veux que Git ignore les changements de permissions et de filemode
```sh
$ git config core.fileMode false
```
Si vous voulez configurer cela comme étant le comportement par défaut pour les utilisateurs connectés, utilisez :
```sh
$ git config --global core.fileMode false
```
### Je veux définir un utilisateur global
Pour configurer des informations d'utilisateur à travers tous les dépôts locaux, et pour configurer un nom qui est identifiable en relisant l'historique :
```sh
$ git config --global user.name “[prénom nom]”
```
Pour configurer une adresse email qui sera associée à chaque ligne de l'historique :
```sh
git config --global user.email “[adresse-email-valide]”
```
## Je n'ai aucune idée de ce que j'ai mal fait
Donc, vous êtes fichu - vous avez `reset` quelque chose, ou vous avez fusionné la mauvaise branche, ou vous avez poussé de force et maintenant vous ne pouvez plus trouver vos commits. Vous savez qu'à un moment donné, tout allait bien, et vous voulez revenir à un état dans lequel vous étiez avant.
C'est là qu'intervient `git reflog`. `reflog` garde trace de tous les changements du bout de la branche, même si ce dernier n'est pas référencé par une branche ou un tag. Fondamentalement, chaque fois que le HEAD change, une nouvelle entrée est ajoutée au reflog. Cela marche seulement pour les dépôts locaux, malheureusement, et ne trace que les mouvements (pas les changements d'un fichier qui n'ont été enregistrés nulle part, par exemple).
```sh
(main)$ git reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
```
Le `reflog` ci-dessus indique un déplacement depuis main vers la branche 2.2 et l'inverse. À partir de là, il y a un hard `reset` vers un commit plus vieux. La dernière activité est représentée en haut et intitulée `HEAD@{0}`.
Si il s'avère que vous êtes accidentellement revenu en arrière, le reflog contiendra le commit sur lequel pointait main (0254ea7) avant que vous ne supprimiez 2 commits par accident.
```sh
$ git reset --hard 0254ea7
```
En utilisant `git reset`, il est ensuite possible de changer main vers le commit vers lequel il pointait. Cela vous donne de la sûreté dans le cas où l'historique a été changé par accident.
(copié et édité depuis [Source](https://www.atlassian.com/git/tutorials/rewriting-history/git-reflog)).
# Autres Ressources
## Livres
* [Learn Enough Git to Be Dangerous](https://www.learnenough.com/git-tutorial) - Un livre de Michael Hartl couvrant les bases de Git
* [Pro Git](https://git-scm.com/book/en/v2) - L'excellent livre de Scott Chacon et Ben Straub à propos de Git
* [Git Internals](https://github.com/pluralsight/git-internals-pdf) - L'autre excellent livre de Scott Chacon à propos de Git
## Tutoriels
* [19 Git Tips For Everyday Use](https://www.alexkras.com/19-git-tips-for-everyday-use) - Une liste de commandes Git utiles qui prennent une ligne.
* [Atlassian's Git tutorial](https://www.atlassian.com/git/tutorials) Comprendre Git avec des tutos à partir du niveau débutant jusqu'au plus avancé.
* [Learn Git branching](https://learngitbranching.js.org/) Un tutoriel interfactif à base de branch/merge/rebase
* [Getting solid at Git rebase vs. merge](https://medium.com/@porteneuve/getting-solid-at-git-rebase-vs-merge-4fa1a48c53aa)
* [Git Commands and Best Practices Cheat Sheet](https://zeroturnaround.com/rebellabs/git-commands-and-best-practices-cheat-sheet) - Un aide-mémoire Git dans un article de blog avec plus d'explications
* [Git from the inside out](https://codewords.recurse.com/issues/two/git-from-the-inside-out) - Un tutoriel qui plonge dans le fonctionnement interne de Git
* [git-workflow](https://github.com/asmeurer/git-workflow) - Le tuto d'[Aaron Meurer](https://github.com/asmeurer) à propos de l'utilisation de Git pour contribuer aux dépôts open source
* [GitHub as a workflow](https://hugogiraudel.com/2015/08/13/github-as-a-workflow/) - Un point de vue intéressant sur l'utilisation de Github en tant que workflow, particulièrement avec des demandes de fusion vides.
* [Githug](https://github.com/Gazler/githug) - Un jeu pour apprendre les workflows Git les plus communs
## Scripts et Outils
* [firstaidgit.io](http://firstaidgit.io/) Un sélection recherchable des questions Git les plus souvent posées
* [git-extra-commands](https://github.com/unixorn/git-extra-commands) - Une collection de scripts Git supplémentaires très utiles
* [git-extras](https://github.com/tj/git-extras) - utilitaires GIT -- résumé de dépôts, repl, population de changelogs, pourcentage de commits par auteur·trice et plus
* [git-fire](https://github.com/qw3rtman/git-fire) - git-fire est un plugin Git qui vient en aide en cas d'urgence en ajoutant tous les fichier actuels, les commitant, et les poussant vers une nouvelle branche (afin d'éviter les conflits).
* [git-tips](https://github.com/git-tips/tips) - Des petites astuces Git
* [git-town](https://github.com/Originate/git-town) - Un support de flux de travail Git générique et de haut-niveau ! http://www.git-town.com
## Clients graphiques
* [GitKraken](https://www.gitkraken.com/) - Le client Git de luxe pour Windows, Mac & Linux
* [git-cola](https://git-cola.github.io/) - Un autre client Git pour Windows et macOS
* [GitUp](https://github.com/git-up/GitUp) - Un client graphique récent qui a une manière dogmatique de gérer les complications de Git
* [gitx-dev](https://rowanj.github.io/gitx/) - un autre client Git graphique pour macOS
* [Sourcetree](https://www.sourcetreeapp.com/) - La simplicité alliée à la puissance dans une belle interface gratuite. Pour Windows et Mac
* [Tower](https://www.git-tower.com/) - Un client graphique pour macOS (payant)
* [tig](https://jonas.github.io/tig/) - terminal text-mode interface for Git
* [Magit](https://magit.vc/) - Une interface pour Git implémentée en tant que paquet Emacs.
* [GitExtensions](https://github.com/gitextensions/gitextensions) - Une extension du shell, un plugin Visual Studio 2010-2015 et un outil de repo Git.
* [Fork](https://git-fork.com/) - Un client Git rapide et amical pour Mac (beta)
* [gmaster](https://gmaster.io/) - Un client Git pour Windows qui permet les fusions à 3 branches, l'analyse de refactorisation, un diff et merge sémantique (beta)
* [gitk](https://git-scm.com/docs/gitk) - Un client Git pour Linux qui permet d'avoir une vue simplifiée de l'état d'un repo.
* [SublimeMerge](https://www.sublimemerge.com/) - Un client ultra rapide et extensible qui donne la possibilité de faire des fusions à 3 branches, de la recherche avancée et de la colorisation syntaxique, en développement actif.
================================================
FILE: README_ja.md
================================================
# Git フライトルール
🌍
*[English](README.md) ∙ [Español](README_es.md) ∙ [Русский](README_ru.md) ∙ [繁體中文](README_zh-TW.md) ∙ [简体中文](README_zh-CN.md) ∙ [한국어](README_kr.md) ∙ [Tiếng Việt](README_vi.md) ∙ [Français](README_fr.md) ∙ [日本語](README_ja.md)*
#### フライトルールとは?
宇宙飛行士(ここでは、Git を使う開発者)が問題に対処するためのガイドです。
> *フライトルール*は、苦心して得た知識体系を、何か起きたときの手順の一つ一つとその理由を記したマニュアルにまとめたものです。基本的に、シナリオごとに標準的な対処法が事細かに説明されています。[...]
> マーキュリー計画のチームが知見を集め始めた 1960 年代から、NASA は私たちの失敗や災難とその解決策を収集してきました。知見の大全は今や、エンジンの不調からハッチハンドルの故障やコンピュータの不具合に至る問題とその対処法に関する、数千ものリストになりました。
— Chris Hadfield, *An Astronaut's Guide to Life*.
#### この文書で使う記法について
文書の全ての例で、明確さのため、現在のブランチとステージされた編集の有無を表示するようカスタマイズされた Bash プロンプトを使います。
括弧内にブランチ名を書き、ブランチ名の横の `*` はステージされた編集があることを示します。
全てのコマンドは Git バージョン 2.13.0 から動くはずです。
Git のバージョンアップについては [Git のウェブサイト](https://www.git-scm.com/) を参照してください。
[](https://gitter.im/k88hudson/git-flight-rules?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
**目次**
- [リポジトリ](#%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA)
- [ローカルリポジトリを初期設定したい](#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92%E5%88%9D%E6%9C%9F%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%9F%E3%81%84)
- [リモートリポジトリをクローンしたい](#%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%B3%E3%81%97%E3%81%9F%E3%81%84)
- [間違ったリモートリポジトリを設定してしまった](#%E9%96%93%E9%81%95%E3%81%A3%E3%81%9F%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F)
- [他の人のリポジトリにコードを書き加えたい](#%E4%BB%96%E3%81%AE%E4%BA%BA%E3%81%AE%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%AB%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B8%E3%81%8D%E5%8A%A0%E3%81%88%E3%81%9F%E3%81%84)
- [プルリクエストでコードを提案するには](#%E3%83%97%E3%83%AB%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%81%A7%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%8F%90%E6%A1%88%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF)
- [フォークしたリポジトリを、元のリポジトリの最新版に合わせて更新したい](#%E3%83%95%E3%82%A9%E3%83%BC%E3%82%AF%E3%81%97%E3%81%9F%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92%E5%85%83%E3%81%AE%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%AE%E6%9C%80%E6%96%B0%E7%89%88%E3%81%AB%E5%90%88%E3%82%8F%E3%81%9B%E3%81%A6%E6%9B%B4%E6%96%B0%E3%81%97%E3%81%9F%E3%81%84)
- [コミットの編集](#%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%AE%E7%B7%A8%E9%9B%86)
- [何をコミットしたかわからなくなった](#%E4%BD%95%E3%82%92%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%97%E3%81%9F%E3%81%8B%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA%E3%81%8F%E3%81%AA%E3%81%A3%E3%81%9F)
- [コミットメッセージに間違った内容を書いてしまった](#%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%81%AB%E9%96%93%E9%81%95%E3%81%A3%E3%81%9F%E5%86%85%E5%AE%B9%E3%82%92%E6%9B%B8%E3%81%84%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F)
- [間違った名前・メールアドレスでコミットしてしまった](#%E9%96%93%E9%81%95%E3%81%A3%E3%81%9F%E5%90%8D%E5%89%8D%E3%83%A1%E3%83%BC%E3%83%AB%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%81%A7%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F)
- [直前のコミットからファイルを削除したい](#%E7%9B%B4%E5%89%8D%E3%81%AE%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%8B%E3%82%89%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%81%9F%E3%81%84)
- [直前のコミットを削除したい](#%E7%9B%B4%E5%89%8D%E3%81%AE%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%81%9F%E3%81%84)
- [特定のコミットを削除したい](#%E7%89%B9%E5%AE%9A%E3%81%AE%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%81%9F%E3%81%84)
- [修正したコミットをリモートにプッシュしようとしたら、エラーメッセージが出た](#%E4%BF%AE%E6%AD%A3%E3%81%97%E3%81%9F%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%82%92%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%81%AB%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%82%88%E3%81%86%E3%81%A8%E3%81%97%E3%81%9F%E3%82%89%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%81%8C%E5%87%BA%E3%81%9F)
- [間違えて hard reset してしまい、元に戻したい](#%E9%96%93%E9%81%95%E3%81%88%E3%81%A6-hard-reset-%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%84%E5%85%83%E3%81%AB%E6%88%BB%E3%81%97%E3%81%9F%E3%81%84)
- [間違えてマージをコミットしてプッシュしてしまった](#%E9%96%93%E9%81%95%E3%81%88%E3%81%A6%E3%83%9E%E3%83%BC%E3%82%B8%E3%82%92%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%97%E3%81%A6%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F)
- [間違えて機密情報を含むファイルをコミットしプッシュしてしまった](#%E9%96%93%E9%81%95%E3%81%88%E3%81%A6%E6%A9%9F%E5%AF%86%E6%83%85%E5%A0%B1%E3%82%92%E5%90%AB%E3%82%80%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%97%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F)
- [大容量のファイルに関する履歴を完全に削除したい](#%E5%A4%A7%E5%AE%B9%E9%87%8F%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E9%96%A2%E3%81%99%E3%82%8B%E5%B1%A5%E6%AD%B4%E3%82%92%E5%AE%8C%E5%85%A8%E3%81%AB%E5%89%8A%E9%99%A4%E3%81%97%E3%81%9F%E3%81%84)
- [おすすめの方法:サードパーティ製の bfg を使う](#%E3%81%8A%E3%81%99%E3%81%99%E3%82%81%E3%81%AE%E6%96%B9%E6%B3%95%E3%82%B5%E3%83%BC%E3%83%89%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E8%A3%BD%E3%81%AE-bfg-%E3%82%92%E4%BD%BF%E3%81%86)
- [ビルトインの方法:git-filter-branch を使う](#%E3%83%93%E3%83%AB%E3%83%88%E3%82%A4%E3%83%B3%E3%81%AE%E6%96%B9%E6%B3%95git-filter-branch-%E3%82%92%E4%BD%BF%E3%81%86)
- [最後のステップ: 変更した履歴をプッシュする](#%E6%9C%80%E5%BE%8C%E3%81%AE%E3%82%B9%E3%83%86%E3%83%83%E3%83%97-%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%9F%E5%B1%A5%E6%AD%B4%E3%82%92%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E3%81%99%E3%82%8B)
- [直近でないコミットの内容を編集したい](#%E7%9B%B4%E8%BF%91%E3%81%A7%E3%81%AA%E3%81%84%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%AE%E5%86%85%E5%AE%B9%E3%82%92%E7%B7%A8%E9%9B%86%E3%81%97%E3%81%9F%E3%81%84)
- [ステージ](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8)
- [バージョン管理されているファイルを全部ステージしたい](#%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E7%AE%A1%E7%90%86%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%85%A8%E9%83%A8%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%97%E3%81%9F%E3%81%84)
- [バージョン管理されているファイルの一部をステージするには](#%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E7%AE%A1%E7%90%86%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%B8%80%E9%83%A8%E3%82%92%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF)
- [ステージされた編集を直前のコミットに追加したい](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%9F%E7%B7%A8%E9%9B%86%E3%82%92%E7%9B%B4%E5%89%8D%E3%81%AE%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%AB%E8%BF%BD%E5%8A%A0%E3%81%97%E3%81%9F%E3%81%84)
- [新しいファイルの全部ではなく一部をステージしたい](#%E6%96%B0%E3%81%97%E3%81%84%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E5%85%A8%E9%83%A8%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F%E4%B8%80%E9%83%A8%E3%82%92%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%97%E3%81%9F%E3%81%84)
- [一つのファイルに加えた編集を二つの異なるコミットに追加したい](#%E4%B8%80%E3%81%A4%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E5%8A%A0%E3%81%88%E3%81%9F%E7%B7%A8%E9%9B%86%E3%82%92%E4%BA%8C%E3%81%A4%E3%81%AE%E7%95%B0%E3%81%AA%E3%82%8B%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%AB%E8%BF%BD%E5%8A%A0%E3%81%97%E3%81%9F%E3%81%84)
- [ステージした編集が多すぎるので、いくつかのコミットに分割したい](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%97%E3%81%9F%E7%B7%A8%E9%9B%86%E3%81%8C%E5%A4%9A%E3%81%99%E3%81%8E%E3%82%8B%E3%81%AE%E3%81%A7%E3%81%84%E3%81%8F%E3%81%A4%E3%81%8B%E3%81%AE%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%AB%E5%88%86%E5%89%B2%E3%81%97%E3%81%9F%E3%81%84)
- [ステージされていない編集をステージし、ステージされた編集のステージを取り消したい](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E7%B7%A8%E9%9B%86%E3%82%92%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%97%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%9F%E7%B7%A8%E9%9B%86%E3%81%AE%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%82%92%E5%8F%96%E3%82%8A%E6%B6%88%E3%81%97%E3%81%9F%E3%81%84)
- [ステージされていない編集](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E7%B7%A8%E9%9B%86)
- [ステージされていない編集を新しいブランチに移したい](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E7%B7%A8%E9%9B%86%E3%82%92%E6%96%B0%E3%81%97%E3%81%84%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AB%E7%A7%BB%E3%81%97%E3%81%9F%E3%81%84)
- [ステージされていない編集を別の既存のブランチに移したい](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E7%B7%A8%E9%9B%86%E3%82%92%E5%88%A5%E3%81%AE%E6%97%A2%E5%AD%98%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AB%E7%A7%BB%E3%81%97%E3%81%9F%E3%81%84)
- [コミットされていないローカルの編集を破棄したい](#%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%81%AE%E7%B7%A8%E9%9B%86%E3%82%92%E7%A0%B4%E6%A3%84%E3%81%97%E3%81%9F%E3%81%84)
- [ステージされていない特定の編集を破棄したい](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E7%89%B9%E5%AE%9A%E3%81%AE%E7%B7%A8%E9%9B%86%E3%82%92%E7%A0%B4%E6%A3%84%E3%81%97%E3%81%9F%E3%81%84)
- [ステージされていない特定のファイルを破棄したい](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E7%89%B9%E5%AE%9A%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E7%A0%B4%E6%A3%84%E3%81%97%E3%81%9F%E3%81%84)
- [ステージされていないローカルな編集だけを破棄したい](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%81%AA%E7%B7%A8%E9%9B%86%E3%81%A0%E3%81%91%E3%82%92%E7%A0%B4%E6%A3%84%E3%81%97%E3%81%9F%E3%81%84)
- [バージョン管理されていないファイルを全て破棄したい](#%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E7%AE%A1%E7%90%86%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%85%A8%E3%81%A6%E7%A0%B4%E6%A3%84%E3%81%97%E3%81%9F%E3%81%84)
- [特定のステージされたファイルのステージを取り消したい](#%E7%89%B9%E5%AE%9A%E3%81%AE%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%9F%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%82%92%E5%8F%96%E3%82%8A%E6%B6%88%E3%81%97%E3%81%9F%E3%81%84)
- [ブランチ](#%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81)
- [全ブランチの一覧を表示したい](#%E5%85%A8%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AE%E4%B8%80%E8%A6%A7%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%97%E3%81%9F%E3%81%84)
- [コミットからブランチを作成する](#%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%8B%E3%82%89%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)
- [間違ったブランチから、あるいは間違ったブランチにプルしてしまった](#%E9%96%93%E9%81%95%E3%81%A3%E3%81%9F%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%8B%E3%82%89%E3%81%82%E3%82%8B%E3%81%84%E3%81%AF%E9%96%93%E9%81%95%E3%81%A3%E3%81%9F%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AB%E3%83%97%E3%83%AB%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F)
- [ローカルのコミットを破棄して、ブランチをサーバ上と同じ状態にしたい](#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%81%AE%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%82%92%E7%A0%B4%E6%A3%84%E3%81%97%E3%81%A6%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E3%82%B5%E3%83%BC%E3%83%90%E4%B8%8A%E3%81%A8%E5%90%8C%E3%81%98%E7%8A%B6%E6%85%8B%E3%81%AB%E3%81%97%E3%81%9F%E3%81%84)
- [新しいブランチではなくマスターブランチにコミットしてしまった](#%E6%96%B0%E3%81%97%E3%81%84%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AB%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F)
- [ファイル全てをリファレンス的な場所に保存しておきたい](#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%85%A8%E3%81%A6%E3%82%92%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9%E7%9A%84%E3%81%AA%E5%A0%B4%E6%89%80%E3%81%AB%E4%BF%9D%E5%AD%98%E3%81%97%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84)
- [別々のブランチにするべき複数のコミットを一つのブランチにしてしまった](#%E5%88%A5%E3%80%85%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AB%E3%81%99%E3%82%8B%E3%81%B9%E3%81%8D%E8%A4%87%E6%95%B0%E3%81%AE%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%82%92%E4%B8%80%E3%81%A4%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AB%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F)
- [upstream で削除されたローカルブランチを削除したい](#upstream-%E3%81%A7%E5%89%8A%E9%99%A4%E3%81%95%E3%82%8C%E3%81%9F%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%81%9F%E3%81%84)
- [間違ってブランチを削除してしまった](#%E9%96%93%E9%81%95%E3%81%A3%E3%81%A6%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F)
- [ブランチを削除したい](#%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%81%9F%E3%81%84)
- [複数のブランチを削除したい](#%E8%A4%87%E6%95%B0%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%81%9F%E3%81%84)
- [ブランチの名前を変更したい](#%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AE%E5%90%8D%E5%89%8D%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%9F%E3%81%84)
- [他の人が作業しているリモートブランチにチェックアウトしたい](#%E4%BB%96%E3%81%AE%E4%BA%BA%E3%81%8C%E4%BD%9C%E6%A5%AD%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AB%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%82%A2%E3%82%A6%E3%83%88%E3%81%97%E3%81%9F%E3%81%84)
- [現在のローカルブランチをもとに新しいリモートブランチを作成したい](#%E7%8F%BE%E5%9C%A8%E3%81%AE%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E3%82%82%E3%81%A8%E3%81%AB%E6%96%B0%E3%81%97%E3%81%84%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%81%9F%E3%81%84)
- [リモートブランチをローカルブランチの upstream に設定したい](#%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AE-upstream-%E3%81%AB%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%9F%E3%81%84)
- [自分の HEAD をデフォルトのリモートブランチを追跡するよう設定したい](#%E8%87%AA%E5%88%86%E3%81%AE-head-%E3%82%92%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%81%AE%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E8%BF%BD%E8%B7%A1%E3%81%99%E3%82%8B%E3%82%88%E3%81%86%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%9F%E3%81%84)
- [間違ったブランチを編集してしまった](#%E9%96%93%E9%81%95%E3%81%A3%E3%81%9F%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E7%B7%A8%E9%9B%86%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F)
- [リベースとマージ](#%E3%83%AA%E3%83%99%E3%83%BC%E3%82%B9%E3%81%A8%E3%83%9E%E3%83%BC%E3%82%B8)
- [リベースやマージを取り消したい](#%E3%83%AA%E3%83%99%E3%83%BC%E3%82%B9%E3%82%84%E3%83%9E%E3%83%BC%E3%82%B8%E3%82%92%E5%8F%96%E3%82%8A%E6%B6%88%E3%81%97%E3%81%9F%E3%81%84)
- [リベースしたが、強制プッシュはしたくない](#%E3%83%AA%E3%83%99%E3%83%BC%E3%82%B9%E3%81%97%E3%81%9F%E3%81%8C%E5%BC%B7%E5%88%B6%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E3%81%AF%E3%81%97%E3%81%9F%E3%81%8F%E3%81%AA%E3%81%84)
- [コミットを統合したい](#%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%82%92%E7%B5%B1%E5%90%88%E3%81%97%E3%81%9F%E3%81%84)
- [安全なマージの方法](#%E5%AE%89%E5%85%A8%E3%81%AA%E3%83%9E%E3%83%BC%E3%82%B8%E3%81%AE%E6%96%B9%E6%B3%95)
- [ブランチを一つのコミットにまとめたい場合](#%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%82%92%E4%B8%80%E3%81%A4%E3%81%AE%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%AB%E3%81%BE%E3%81%A8%E3%82%81%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88)
- [プッシュされていないコミットのみを統合したい場合](#%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%AE%E3%81%BF%E3%82%92%E7%B5%B1%E5%90%88%E3%81%97%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88)
- [マージを中止したい](#%E3%83%9E%E3%83%BC%E3%82%B8%E3%82%92%E4%B8%AD%E6%AD%A2%E3%81%97%E3%81%9F%E3%81%84)
- [ブランチの親コミットを更新したい](#%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AE%E8%A6%AA%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%82%92%E6%9B%B4%E6%96%B0%E3%81%97%E3%81%9F%E3%81%84)
- [ブランチの全コミットがマージされているか確認する](#%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AE%E5%85%A8%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%8C%E3%83%9E%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%8B%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B)
- [対話的リベースで起こりうる問題](#%E5%AF%BE%E8%A9%B1%E7%9A%84%E3%83%AA%E3%83%99%E3%83%BC%E3%82%B9%E3%81%A7%E8%B5%B7%E3%81%93%E3%82%8A%E3%81%86%E3%82%8B%E5%95%8F%E9%A1%8C)
- [リベース編集画面に 'noop' と表示される](#%E3%83%AA%E3%83%99%E3%83%BC%E3%82%B9%E7%B7%A8%E9%9B%86%E7%94%BB%E9%9D%A2%E3%81%AB-noop-%E3%81%A8%E8%A1%A8%E7%A4%BA%E3%81%95%E3%82%8C%E3%82%8B)
- [コンフリクトがあった](#%E3%82%B3%E3%83%B3%E3%83%95%E3%83%AA%E3%82%AF%E3%83%88%E3%81%8C%E3%81%82%E3%81%A3%E3%81%9F)
- [スタッシュ](#%E3%82%B9%E3%82%BF%E3%83%83%E3%82%B7%E3%83%A5)
- [全ての編集をスタッシュしたい](#%E5%85%A8%E3%81%A6%E3%81%AE%E7%B7%A8%E9%9B%86%E3%82%92%E3%82%B9%E3%82%BF%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%81%9F%E3%81%84)
- [特定のファイルをスタッシュしたい](#%E7%89%B9%E5%AE%9A%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%82%B9%E3%82%BF%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%81%9F%E3%81%84)
- [メッセージをつけてスタッシュしたい](#%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%82%92%E3%81%A4%E3%81%91%E3%81%A6%E3%82%B9%E3%82%BF%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%81%9F%E3%81%84)
- [一覧から特定のスタッシュを選んで適用したい](#%E4%B8%80%E8%A6%A7%E3%81%8B%E3%82%89%E7%89%B9%E5%AE%9A%E3%81%AE%E3%82%B9%E3%82%BF%E3%83%83%E3%82%B7%E3%83%A5%E3%82%92%E9%81%B8%E3%82%93%E3%81%A7%E9%81%A9%E7%94%A8%E3%81%97%E3%81%9F%E3%81%84)
- [ステージされていない編集をそのままにしつつ、スタッシュしたい](#%E3%82%B9%E3%83%86%E3%83%BC%E3%82%B8%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%81%AA%E3%81%84%E7%B7%A8%E9%9B%86%E3%82%92%E3%81%9D%E3%81%AE%E3%81%BE%E3%81%BE%E3%81%AB%E3%81%97%E3%81%A4%E3%81%A4%E3%82%B9%E3%82%BF%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%81%9F%E3%81%84)
- [検索](#%E6%A4%9C%E7%B4%A2)
- [全コミットから文字列を検索したい](#%E5%85%A8%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%8B%E3%82%89%E6%96%87%E5%AD%97%E5%88%97%E3%82%92%E6%A4%9C%E7%B4%A2%E3%81%97%E3%81%9F%E3%81%84)
- [author または committer から検索する](#author-%E3%81%BE%E3%81%9F%E3%81%AF-committer-%E3%81%8B%E3%82%89%E6%A4%9C%E7%B4%A2%E3%81%99%E3%82%8B)
- [特定のファイルを含むコミットの一覧を表示したい](#%E7%89%B9%E5%AE%9A%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%90%AB%E3%82%80%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%AE%E4%B8%80%E8%A6%A7%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%97%E3%81%9F%E3%81%84)
- [特定の関数に関するコミット履歴を見たい](#%E7%89%B9%E5%AE%9A%E3%81%AE%E9%96%A2%E6%95%B0%E3%81%AB%E9%96%A2%E3%81%99%E3%82%8B%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E5%B1%A5%E6%AD%B4%E3%82%92%E8%A6%8B%E3%81%9F%E3%81%84)
- [コミットが参照されているタグを検索したい](#%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E3%81%8C%E5%8F%82%E7%85%A7%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%82%BF%E3%82%B0%E3%82%92%E6%A4%9C%E7%B4%A2%E3%81%97%E3%81%9F%E3%81%84)
- [サブモジュール](#%E3%82%B5%E3%83%96%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB)
- [全てのサブモジュールをクローンする](#%E5%85%A8%E3%81%A6%E3%81%AE%E3%82%B5%E3%83%96%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%92%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%B3%E3%81%99%E3%82%8B)
- [サブモジュールを削除する](#%E3%82%B5%E3%83%96%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B)
- [その他色々](#%E3%81%9D%E3%81%AE%E4%BB%96%E8%89%B2%E3%80%85)
- [あるブランチから別のブランチにフォルダをコピーしたい](#%E3%81%82%E3%82%8B%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%8B%E3%82%89%E5%88%A5%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%AB%E3%83%95%E3%82%A9%E3%83%AB%E3%83%80%E3%82%92%E3%82%B3%E3%83%94%E3%83%BC%E3%81%97%E3%81%9F%E3%81%84)
- [削除されたファイルを復元したい](#%E5%89%8A%E9%99%A4%E3%81%95%E3%82%8C%E3%81%9F%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%BE%A9%E5%85%83%E3%81%97%E3%81%9F%E3%81%84)
- [タグを削除したい](#%E3%82%BF%E3%82%B0%E3%82%92%E5%89%8A%E9%99%A4%E3%81%97%E3%81%9F%E3%81%84)
- [削除されたタグを復元したい](#%E5%89%8A%E9%99%A4%E3%81%95%E3%82%8C%E3%81%9F%E3%82%BF%E3%82%B0%E3%82%92%E5%BE%A9%E5%85%83%E3%81%97%E3%81%9F%E3%81%84)
- [削除されたパッチを取得したい](#%E5%89%8A%E9%99%A4%E3%81%95%E3%82%8C%E3%81%9F%E3%83%91%E3%83%83%E3%83%81%E3%82%92%E5%8F%96%E5%BE%97%E3%81%97%E3%81%9F%E3%81%84)
- [リポジトリを zip ファイルとしてエクスポートしたい](#%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%82%92-zip-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%A8%E3%81%97%E3%81%A6%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88%E3%81%97%E3%81%9F%E3%81%84)
- [同じ名前のブランチとタグをプッシュしたい](#%E5%90%8C%E3%81%98%E5%90%8D%E5%89%8D%E3%81%AE%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E3%81%A8%E3%82%BF%E3%82%B0%E3%82%92%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%81%9F%E3%81%84)
- [ファイルの追跡](#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E8%BF%BD%E8%B7%A1)
- [ファイルの内容は変えずに、ファイル名の大文字・小文字を変更したい](#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E5%86%85%E5%AE%B9%E3%81%AF%E5%A4%89%E3%81%88%E3%81%9A%E3%81%AB%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E5%90%8D%E3%81%AE%E5%A4%A7%E6%96%87%E5%AD%97%E3%83%BB%E5%B0%8F%E6%96%87%E5%AD%97%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%9F%E3%81%84)
- [git pull してローカルのファイルを上書きしたい](#git-pull-%E3%81%97%E3%81%A6%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E4%B8%8A%E6%9B%B8%E3%81%8D%E3%81%97%E3%81%9F%E3%81%84)
- [ファイルを残しつつ Git から削除したい](#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E6%AE%8B%E3%81%97%E3%81%A4%E3%81%A4-git-%E3%81%8B%E3%82%89%E5%89%8A%E9%99%A4%E3%81%97%E3%81%9F%E3%81%84)
- [ファイルを特定の版まで差し戻したい](#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E7%89%B9%E5%AE%9A%E3%81%AE%E7%89%88%E3%81%BE%E3%81%A7%E5%B7%AE%E3%81%97%E6%88%BB%E3%81%97%E3%81%9F%E3%81%84)
- [特定のファイルのコミット間・ブランチ間の差分を表示したい](#%E7%89%B9%E5%AE%9A%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%82%B3%E3%83%9F%E3%83%83%E3%83%88%E9%96%93%E3%83%BB%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E9%96%93%E3%81%AE%E5%B7%AE%E5%88%86%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%97%E3%81%9F%E3%81%84)
- [特定のファイルの変更を無視したい](#%E7%89%B9%E5%AE%9A%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E5%A4%89%E6%9B%B4%E3%82%92%E7%84%A1%E8%A6%96%E3%81%97%E3%81%9F%E3%81%84)
- [Git によるデバッグ](#git-%E3%81%AB%E3%82%88%E3%82%8B%E3%83%87%E3%83%90%E3%83%83%E3%82%B0)
- [設定](#%E8%A8%AD%E5%AE%9A)
- [Git コマンドにエイリアスを設定したい](#git-%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%AB%E3%82%A8%E3%82%A4%E3%83%AA%E3%82%A2%E3%82%B9%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%9F%E3%81%84)
- [空のディレクトリをリポジトリに加えたい](#%E7%A9%BA%E3%81%AE%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%82%92%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%AB%E5%8A%A0%E3%81%88%E3%81%9F%E3%81%84)
- [リポジトリへのユーザ名とパスワードをキャッシュしたい](#%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%B8%E3%81%AE%E3%83%A6%E3%83%BC%E3%82%B6%E5%90%8D%E3%81%A8%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E3%82%92%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%97%E3%81%9F%E3%81%84)
- [パーミッションとファイルモードの変更を Git が無視するようにしたい](#%E3%83%91%E3%83%BC%E3%83%9F%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%A2%E3%83%BC%E3%83%89%E3%81%AE%E5%A4%89%E6%9B%B4%E3%82%92-git-%E3%81%8C%E7%84%A1%E8%A6%96%E3%81%99%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%97%E3%81%9F%E3%81%84)
- [グローバルユーザを設定したい](#%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E3%83%A6%E3%83%BC%E3%82%B6%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%9F%E3%81%84)
- [何を間違ったかわからないとき](#%E4%BD%95%E3%82%92%E9%96%93%E9%81%95%E3%81%A3%E3%81%9F%E3%81%8B%E3%82%8F%E3%81%8B%E3%82%89%E3%81%AA%E3%81%84%E3%81%A8%E3%81%8D)
- [Git ショートカット](#git-%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%82%AB%E3%83%83%E3%83%88)
- [Git Bash](#git-bash)
- [Windows の PowerShell](#windows-%E3%81%AE-powershell)
- [文献](#%E6%96%87%E7%8C%AE)
- [本](#%E6%9C%AC)
- [チュートリアル](#%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB)
- [スクリプトとツール](#%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%A8%E3%83%84%E3%83%BC%E3%83%AB)
- [GUI クライアント](#gui-%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88)
## リポジトリ
### ローカルリポジトリを初期設定したい
既存のディレクトリを Git リポジトリとして初期設定するには、次を実行します。
```sh
(my-folder) $ git init
```
### リモートリポジトリをクローンしたい
リモートリポジトリをクローン(コピー)したいときは、リポジトリの URL をコピーし、次を実行します。
```sh
$ git clone [url]
```
すると、リモートリポジトリと同名のフォルダにリポジトリの内容が保存されます。
リモートリポジトリのあるサーバに接続できる必要があります。大抵の場合インターネット接続があれば大丈夫です。
リモートリポジトリと異なる名前のフォルダにクローンしたいときは、次のようにします。
```sh
$ git clone [url] name-of-new-folder
```
### 間違ったリモートリポジトリを設定してしまった
問題はいくつかの場合に分けられます。
間違ったリポジトリをクローンしてしまったときは、`git clone` で作ったディレクトリを削除して、正しいリポジトリをクローンし直せばよいです。
間違ったリポジトリを既存のローカルリポジトリの origin に設定してしまったときは、次のように origin の URL を変更しましょう。
```sh
$ git remote set-url origin [url of the actual repo]
```
ほかの問題は[この StackOverflow トピック](https://stackoverflow.com/questions/2432764/how-to-change-the-uri-url-for-a-remote-git-repository#2432799)を参照してください。
### 他の人のリポジトリにコードを書き加えたい
Git では、アクセス権がないかぎり他の人のリポジトリに書き込むことはできません。
GitHub は Git リポジトリのホスティングサービスであって Git 自体とは異なるものですが、GitHub でもやはり同様です。
しかし、パッチでコードを提案することができます。
GitHub ならフォークとプルリクエストの機能がこれにあたります。
まずはフォークについて説明しましょう。
フォークはリポジトリのコピーです。
Git 自体の機能ではないものの、GitHub, BitBucket, GitLab やその他のホスティングサービスにはこの機能があり、各サービスの UI を通して実行できます。
#### プルリクエストでコードを提案するには
リポジトリをフォークしたら、ローカルマシンにクローンして編集しましょう。
ちょっとした編集なら GitHub 上でもできるでしょうが、この文書は GitHub フライトルールではないので、ローカルで編集する方法を説明します。
```sh
# ssh を使う場合
$ git clone git@github.com:k88hudson/git-flight-rules.git
# https を使う場合
$ git clone https://github.com/k88hudson/git-flight-rules.git
```
できたディレクトリに `cd` で移動し、`git remote` を実行してください。
リモートのリストが表示されるはずです。
ただ、おそらく表示されるのは `k88hudson/git-flight-rules` を参照する `origin` だけなので、自分がフォークして作った方のリモートも用意する必要があります。
Git では、自分自身のリポジトリのリモートは `origin`、フォークした元のリポジトリには `upstream` と名付けるのが一般的です。
これにならって、まず、リモート `origin` の名前を `upstream` に変更しましょう。
```sh
$ git remote rename origin upstream
```
実は `git remote set-url` でも同じことができますが、時間と手間が余計にかかります。
次に、自分のプロジェクトを参照する新しいリモートを作成します。
```sh
$ git remote add origin git@github.com:YourName/git-flight-rules.git
```
この時点でリモートは二つです。
- `origin` は自分のリポジトリを参照しています。
- `upstream` は元のリポジトリを参照しています。
`origin` は読み取り・書き込みの両方ができ、`upstream` は読み取り専用です。
編集が済んだら、編集を(通常はブランチ内から)リモート `origin` にプッシュしましょう。
ブランチ内にいる場合、次のように `--set-upstream` を使うと、次回から同じブランチからプッシュする際にリモートを指定せずに済みます。
```sh
$ (feature/my-feature) git push --set-upstream origin feature/my-feature
```
Git で CLI からプルリクエストを送る方法はありません([hub](http://github.com/github/hub) のようなツールを使えば別ですが)。
プルリクエストを送りたいときは、GitHub(あるいは他のホスティングサービス)上でプルリクエストを作成してください。
元のリポジトリとフォークしたリポジトリの紐付けはホスティングサービスが自動的にしてくれます。
プルリクエストの後、コードレビューのフィードバックに対応するのを忘れないようにしましょう。
#### フォークしたリポジトリを、元のリポジトリの最新版に合わせて更新したい
そのうち `upstream` リポジトリが更新され、自分の `origin` にプルしたくなるかもしれません。
自分だけでなく他の人も共同作業していることを忘れないようにしてください。
自分のフィーチャーブランチにいて、これを元のリポジトリに合わせて更新したい場合を想定します。
元のプロジェクトを参照するリモートは設定してありますか? まだなら今やってしまいましょう。
通常はリモートの名前に `upstream` を使います。
```sh
$ (main) git remote add upstream
# $ (main) git remote add upstream git@github.com:k88hudson/git-flight-rules.git
```
これで `upstream` から最新版を取得できるようになりました。
```sh
$ (main) git fetch upstream
$ (main) git merge upstream/main
# コマンド一つでもできる
$ (main) git pull upstream main
```
## コミットの編集
### 何をコミットしたかわからなくなった
何も考えず `git commit -a` で編集をコミットしてしまい、その内容がわからないとします。
現在の HEAD の最新のコミット内容は次のように表示できます。
```sh
(main)$ git show
```
もしくは次の通りです。
```sh
$ git log -n1 -p
```
特定のコミットの時点のファイルの中身を見たいときは次のようにします(`` は見たいコミット)。
```sh
$ git show :filename
```
### コミットメッセージに間違った内容を書いてしまった
コミットメッセージに間違った内容を書いてしまったとします。
コミットがまだプッシュされていない場合は、次のようにして編集は変えずにコミットメッセージを編集できます。
```sh
$ git commit --amend --only
```
デフォルトのテキストエディタが開き、コミットメッセージを編集できます。
次のようにして、一つのコマンドでいっぺんにやることもできます。
```sh
$ git commit --amend --only -m 'xxxxxxx'
```
すでにコミットをプッシュしてしまった場合、コミットを修正して強制プッシュすることはできますが、おすすめしません。
### 間違った名前・メールアドレスでコミットしてしまった
コミットが一つだけなら、次のように修正します。
```sh
$ git commit --amend --no-edit --author "New Authorname "
```
あるいは、名前とメールアドレスを `git config --global author.(name|email)` で正しく設定してから、次を実行します。
```sh
$ git commit --amend --reset-author --no-edit
```
履歴すべてについて変更したい場合は、`git filter-branch` の man ページを参照してください。
### 直前のコミットからファイルを削除したい
直前のコミットから特定のファイルに関する編集を削除するには次のようにします。
```sh
$ git checkout HEAD^ myfile
$ git add myfile
$ git commit --amend --no-edit
```
直前のコミットで新たに追加したファイルを(Git のみから)削除したいときは次の通りです。
```sh
$ git rm --cached myfile
$ git commit --amend --no-edit
```
このコマンドは、不要なファイルをパッチにコミットしてしまい、強制プッシュでリモートのパッチを更新したいときに特に便利です。
オプション `--no-edit` は既存のコミットメッセージを変更しないようにするためのものです。
### 直前のコミットを削除したい
すでにプッシュしたコミットを削除するには次のようにします。
ただし、編集履歴が不可逆的に変更され、リポジトリから変更内容をプルしてしまった他の人の編集履歴は滅茶苦茶になります。
要するに、よくわからない場合は絶対にしないでください。
```sh
$ git reset HEAD^ --hard
$ git push --force-with-lease [remote] [branch]
```
まだコミットをプッシュしていない場合は、次のようにして(ステージされた編集はそのままで)直前のコミットをする前の状態に Git をリセットできます。
```
(my-branch)$ git reset --soft HEAD^
```
これはプッシュしていない場合にのみ有効な方法です。
プッシュしてしまった場合、本当に安全な方法は `git revert SHAofBadCommit` だけです。
このコマンドは、直前のコミットを相殺するようなコミットを新たに作成します。
プッシュしたブランチがリベースについて安全である場合(つまり、他の開発者がプルすることを想定していない場合)は、`git push --force-with-lease` を使っても大丈夫です。
### 特定のコミットを削除したい
上と同様に、やむを得ない場合以外絶対に行わないでください。
```sh
$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push --force-with-lease [remote] [branch]
```
あるいは、[対話的 rebase](#interactive-rebase) で削除したいコミットに対応する行を選択して削除します。
### 修正したコミットをリモートにプッシュしようとしたら、エラーメッセージが出た
```sh
To https://github.com/yourusername/repo.git
! [rejected] mybranch -> mybranch (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```
amend による修正は、rebase と同様に(後述)、**古いコミットを新たなコミットで置き換えます**。
それゆえ、修正前のコミットをすでにリモートにプッシュしてしまった場合は、強制プッシュ (`--force-with-lease`) しなければいけません。
強制プッシュには細心の注意が必要です。
*必ず*ブランチを指定するように!
```sh
(my-branch)$ git push origin mybranch --force-with-lease
```
一般論として、**強制プッシュは避けましょう**。
修正したコミットを強制プッシュするよりは、新たなコミットを作ってプッシュするのがベストです。
強制プッシュは、対象のブランチやその子ブランチで作業した他の開発者のソース履歴にコンフリクトをきたします。
誰かが同じブランチで作業していて、強制プッシュがその人の編集を上書きしてしまう場合は、`--force-with-lease` も失敗します。
他の誰も同じブランチで作業していないことが*絶対に*確実な場合、あるいはブランチの一部を*無条件で*更新したい場合は `--force` (`-f`) で行うことができますが、これは原則として避けるべきです。
### 間違えて hard reset してしまい、元に戻したい
間違えて `git reset --hard` をしてしまっても、大抵はコミットを復元できます。
Git は数日間のログを全て残してくれているからです。
注意:これは作業がバックアップされている場合、つまりコミットないしスタッシュされている場合に限ります。
`git reset --hard` はコミットされていない変更を*削除*してしまうので、注意して使ってください。
(安全なのは `git reset --keep` を使うことです。)
```sh
(main)$ git reflog
```
過去のコミットとリセットに対応するコミットが表示されるので、復元したいコミットの SHA を選んでリセットします。
```sh
(main)$ git reset --hard SHA1234
```
これで大丈夫です。
### 間違えてマージをコミットしてプッシュしてしまった
マージの準備ができていないフィーチャーブランチをメインのブランチにマージしてしまったときは、マージを取り消すことができます。
ただし注意すべき点は、マージコミットには複数(通常は二つ)の親があることです。
次のコマンドを実行します。
```sh
(feature-branch)$ git revert -m 1
```
ここでオプション `-m 1` は差し戻す先の親として親 1(マージした先のブランチ)を指定します。
注意:親の番号はコミット ID とは異なります。
マージコミットの行は `Merge: 8e2ce2d 86ac2e7` のようになっています。
親番号はこのコミットの親を指定する 1 から始まる番号で、最初の番号は 1 番、次は 2 番、のように振られます。
### 間違えて機密情報を含むファイルをコミットしプッシュしてしまった
機密情報やプライベートな情報(パスワードやキー等)を含むデータを誤ってプッシュしてしまった場合、コミットを修正できます。
ただし、ひとたびデータをコミットしてプッシュしてしまったら、その内容は盗み取られるおそれがあることに留意してください。
下の手順で公開リポジトリやローカルからデータを削除できますが、他の誰かがすでにプルしたデータを削除することは**不可能です**。
パスワードをコミットしてしまった場合は**直ちに変更してください**。
キーをコミットしてしまった場合は**直ちに再生成しましょう**。
誰かがすでに機密情報をプルしてしまった可能性がある限り、プッシュしたコミットを修正するだけでは不十分です。
ファイルを編集して機密情報を削除したあと、次を実行します。
```sh
(feature-branch)$ git add edited_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
ファイルごと削除したいがローカルには残しておきたい場合、次を実行します。
```sh
(feature-branch)$ git rm --cached sensitive_file
echo sensitive_file >> .gitignore
(feature-branch)$ git add .gitignore
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
あるいは、機密情報をローカルの環境変数に保存しておきましょう。
ファイルごと削除した上でローカルからも削除したい場合は、次を実行します。
```sh
(feature-branch)$ git rm sensitive_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
すでに他のコミットをしてしまった場合(つまり、機密情報のコミットが直前のコミットよりも前である場合)は、リベースする必要があります。
### 大容量のファイルに関する履歴を完全に削除したい
削除したいファイルが機密情報である場合は[機密情報を削除する方法](#undo-sensitive-commit-push)を参照してください。
コミットで大容量のファイルや不要なファイルを削除しても、`.git` フォルダの Git 履歴には残るので、`git clone` したときに余計なファイルまでダウンロードしてしまうことになります。
ここで説明する手順には強制プッシュを必要とし、リポジトリ履歴を大きく変更してしまいます。
誰かがリモートで共同作業している場合は、全員のローカルの編集履歴がプッシュされていることを確認しておいてください。
履歴を書き換えるには二つの方法があります。
ビルトインの `git-filter-branch` と [`bfg-repo-cleaner`](https://rtyley.github.io/bfg-repo-cleaner/) です。
`bfg` はエレガントで性能がよい一方、サードパーティ製のソフトをダウンロードしなければならず、Java も必要です。
ここでは両方を説明します。
最後のステップでは強制プッシュをしますが、リポジトリの履歴の大部分を永久に変更するため、通常の強制プッシュよりもなお特別な配慮が必要になります。
#### おすすめの方法:サードパーティ製の bfg を使う
bfg-repo-cleaner を使うには Java が必要です。
[ここ](https://rtyley.github.io/bfg-repo-cleaner/)から bfg の jar ファイルをダウンロードしてください。
下の例では `bfg.jar` を使いますが、ダウンロードしたものには `bfg-1.13.0.jar` のようにバージョン番号がついているかもしれません。
特定のファイルを削除する場合は次のようにします。
```sh
(main)$ git rm path/to/filetoremove
(main)$ git commit -m "Commit removing filetoremove"
(main)$ java -jar ~/Downloads/bfg.jar --delete-files filetoremove
```
なお、bfg を使うときは、ファイルがサブディレクトリにあってもそのままのファイル名を入力することに注意してください。
パターンからファイルを削除することもできます。例えば次の通りです。
```sh
(main)$ git rm *.jpg
(main)$ git commit -m "Commit removing *.jpg"
(main)$ java -jar ~/Downloads/bfg.jar --delete-files *.jpg
```
bfg は最新のコミットにあるファイルには影響しません。
例えば、リポジトリに複数あった大容量の .tga ファイルのうち一部を以前のコミットで削除したとして、bfg を実行しても最新のコミットにあるファイルはそのままです。
なお、コミットでファイル名を変更した場合、例えばもともと `LargeFileFirstName.mp4` だったファイルが後のコミットで `LargeFileSecondName.mp4` に変更されている場合は、`java -jar ~/Downloads/bfg.jar --delete-files LargeFileSecondName.mp4` を実行しても Git の履歴からは削除されません。
両方のファイル名それぞれについて `--delete-files` を実行するか、パターンマッチで両方削除してください。
#### ビルトインの方法:git-filter-branch を使う
`git-filter-branch` はややこしくて機能も貧弱ですが、`bfg` のインストールや実行ができなくても使えます。
以下では、`filepattern` を名前やパターン(`*.jpg` など)に置き換えてください。
パターンにマッチしたファイルの履歴が全ての履歴とブランチから削除されます。
```sh
(main)$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch filepattern' --prune-empty --tag-name-filter cat -- --all
```
ここで使っている `--tag-name-filter cat` は煩雑ですが、このように `cat` を使うのが元のタグを新しいコミットにつける最も簡単な方法です。
また、`--prune-empty` は現在空のコミットを全て削除します。
#### 最後のステップ: 変更した履歴をプッシュする
ファイルを削除したら、リポジトリのものを壊していないか慎重に確認してください。
何か壊してしまった場合は、リポジトリを再度クローンしてやり直すのが最も簡単です。
最後のステップとして、必要に応じて Git ガベージコレクションで .git フォルダの容量を最小化してから、強制プッシュします。
```sh
(main)$ git reflog expire --expire=now --all && git gc --prune=now --aggressive
(main)$ git push origin --force --tags
```
リポジトリの履歴を全て書き換えているので、`git push` の量が膨大すぎて `“The remote end hung up unexpectedly”` というエラーが返るかもしれません。
その場合は Git の post buffer を増やしてみます。
```sh
(main)$ git config http.postBuffer 524288000
(main)$ git push --force
```
うまくいかない場合は、コミットを手作業で小分けにしてプッシュします。
プッシュが成功するまで、`` を増やしながら次のコマンドを試してください。
```sh
(main)$ git push -u origin HEAD~:refs/head/main --force
```
プッシュが成功したら、通常の`git push` が 成功するまで `` を徐々に減らしてください。
### 直近でないコミットの内容を編集したい
複数(たとえば三件)のコミットを行ったあと、文脈的に最初のコミットに属する作業をし忘れたことに気づいたとします。
この作業を新たなコミットとして行えばコードベースは綺麗に保てるものの、コミットがアトミックでなくなってしまう(同じ文脈の作業が同じコミットに属さない)ので、この状況は厄介です。
し忘れた作業が属するべきコミットを編集して作業を取り入れつつ、その後のコミットには手をつけないようにしたいとき、`git rebase` が役に立ちます。
最後から三件目のコミットを編集したいとします。
```sh
(your-branch)$ git rebase -i HEAD~4
```
上のコマンドで対話的リベースモードに入り、直近三件のコミットを編集できるようになります。
テキストエディタが開き、次のような内容が表示されます。
```sh
pick 9e1d264 The third last commit
pick 4b6e19a The second to last commit
pick f4037ec The last commit
```
これを次のように編集します。
```sh
edit 9e1d264 The third last commit
pick 4b6e19a The second to last commit
pick f4037ec The last commit
```
これは最後から三件目のコミットを編集しつつ、他の二件はそのままにするよう `rebase` に指示するコマンドです。
テキストエディタを保存して終了したら、Git がリベースを始めます。指定したコミットで止まり、そのコミットを編集できるようになります。
これで最初にコミットしたときにし忘れた作業を適用できます。編集とステージによって適用しましょう。
その後、次を実行します。
```sh
(your-branch)$ git commit --amend
```
これはコミットメッセージはそのままでコミットを作り直すよう Git に指示するコマンドです。
これで面倒な作業は終わりです。
```sh
(your-branch)$ git rebase --continue
```
あとは上を実行すれば完了です。
## ステージ
### バージョン管理されているファイルを全部ステージしたい
```sh
$ git add -u
```
#### バージョン管理されているファイルの一部をステージするには
```sh
# 拡張子が .txt のファイルをステージする
$ git add -u *.txt
# src ディレクトリ内の全ファイルをステージする
$ git add -u src/
```
### ステージされた編集を直前のコミットに追加したい
```sh
(my-branch*)$ git commit --amend
```
コミットメッセージを変更したくないときは、コミットメッセージを再利用するよう Git に指示します。
```sh
(my-branch*)$ git commit --amend -C HEAD
```
### 新しいファイルの全部ではなく一部をステージしたい
通常、ファイルの一部をステージするには次を実行します。
```sh
$ git add --patch filename.x
```
短縮形は `-p` です。これにより対話モードが開きます。
オプション `s` をつけるとコミットを分割 (split) できます。ただし、新しく作ったファイルの場合このオプションは使えません。
ファイルを新たに追加するには、次を実行します。
```sh
$ git add -N filename.x
```
オプション `e` を使うと、どの行を追加するか手動で選択することができます。
コマンド `git diff --cached` あるいは `git diff --staged` を実行すると、ステージした行がローカルに保存されたものと比較して表示されます。
### 一つのファイルに加えた編集を二つの異なるコミットに追加したい
コマンド `git add` はファイル全体をコミットに追加します。
また、`git add -p` を使うと、どの編集を追加するか対話的に選択できます。
### ステージした編集が多すぎるので、いくつかのコミットに分割したい
コマンド `git reset -p` を実行すると、パッチモードのリセットダイアログが開きます。
なお、`git add -p` と似ていますが、"yes" がステージを取り消して次のコミットから除去することを意味する点で異なります。
### ステージされていない編集をステージし、ステージされた編集のステージを取り消したい
通常は、ステージされたファイルのステージを一旦全部取り消したあと、コミットしたいものをピックするべきです。
ステージされている編集とされていない編集を切り替えたいときは、ステージされた編集を記録しておく仮のコミットを作成し、ステージされていないファイルをステージしてスタッシュします。
それから仮のコミットをリセットして、スタッシュを pop します。
```sh
$ git commit -m "WIP"
$ git add . # バージョン管理されていないファイルも追加される
$ git stash
$ git reset HEAD^
$ git stash pop --index 0
```
注意 1:ここで `pop` を使うのは、操作を複数回行ってもなるべく結果が変わらないようにするためです。
注意 2:ここで `--index` を指定しないと、ステージされたファイルはステージされていない扱いになります(理由は[このリンク](https://stackoverflow.com/questions/31595873/git-stash-with-staged-files-does-stash-convert-staged-files-to-unstaged?answertab=active#tab-top)を参照してください)。
## ステージされていない編集
### ステージされていない編集を新しいブランチに移したい
```sh
$ git checkout -b my-branch
```
### ステージされていない編集を別の既存のブランチに移したい
```sh
$ git stash
$ git checkout my-branch
$ git stash pop
```
### コミットされていないローカルの編集を破棄したい
ステージされている編集とされていない編集の両方を全て破棄したいときは、次のようにします。
```sh
(my-branch)$ git reset --hard
# または
(main)$ git checkout -f
```
次のコマンドは `git add` でステージした全ファイルのステージを取り消します。
```sh
$ git reset
```
次のコマンドはコミットされていないローカルの編集を全て差し戻します(リポジトリのルートで実行する必要があります)。
```sh
$ git checkout .
```
特定のファイルやディレクトリについて、コミットされていない編集を差し戻すこともできます。
```sh
$ git checkout [some_dir|file.txt]
```
コミットされていない編集を全て差し戻すのには次の方法もあります(コマンドが長いですが、全てのサブディレクトリから実行できます)。
```sh
$ git reset --hard HEAD
```
次を実行するとローカルのバージョン管理されていないファイルが全て削除されます。
つまり、Git で管理されているファイルだけ残ります。
```sh
$ git clean -fd
```
Git に無視されるファイルも全て取り除くには `-x` を指定します。
### ステージされていない特定の編集を破棄したい
ワークツリーの編集の全部ではなく一部だけを破棄したい場合です。
残したい編集だけを残し、残したくない編集をチェックアウトします。
```sh
$ git checkout -p
# 破棄したいコードすべてについて y と答える
```
もう一つの方法は `stash` を使います。残したい編集をスタッシュし、ワークツリーをリセットして、残したい編集を適用します。
```sh
$ git stash -p
# 残したいコードを全て選ぶ
$ git reset --hard
$ git stash pop
```
あるいは、残したくない編集をスタッシュして、スタッシュを破棄してもよいです。
```sh
$ git stash -p
# 残したくないコードを全て選ぶ
$ git stash drop
```
### ステージされていない特定のファイルを破棄したい
ワークツリーの特定のファイル一つを取り除きたいときです。
```sh
$ git checkout myFile
```
ワークツリー上の複数のファイルを破棄したいときは、破棄したいファイルを列挙します。
```sh
$ git checkout myFirstFile mySecondFile
```
### ステージされていないローカルな編集だけを破棄したい
コミットもステージもされていないローカルの編集を全て破棄したいときは、次を実行します。
```sh
$ git checkout .
```
### バージョン管理されていないファイルを全て破棄したい
バージョン管理されていないファイルを全て破棄したいときは、次を実行します。
```sh
$ git clean -f
```
### 特定のステージされたファイルのステージを取り消したい
間違えてステージしたが、コミットはしていないファイルが一つまたは複数ある場合です。
そのステージを取り消すには次のようにします。
```sh
$ git reset --
```
ファイルのステージが取り消され、バージョン管理されていないものとみなされます。
## ブランチ
### 全ブランチの一覧を表示したい
ローカルブランチの一覧を表示
```sh
$ git branch
```
リモートブランチの一覧を表示
```sh
$ git branch -r
```
ローカルとリモート両方のブランチの一覧を表示
```sh
$ git branch -a
```
### コミットからブランチを作成する
```sh
$ git checkout -b
```
### 間違ったブランチから、あるいは間違ったブランチにプルしてしまった
これも `git reflog` を使う場面です。
間違ったプルの前に HEAD が参照していたものを表示します。
```sh
(main)$ git reflog
ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward
c5bc55a HEAD@{1}: checkout: checkout message goes here
```
ブランチを適切なコミットにリセットするだけです。
```sh
$ git reset --hard c5bc55a
```
これで完了です。
### ローカルのコミットを破棄して、ブランチをサーバ上と同じ状態にしたい
サーバに編集をプッシュしていないことを確認してください。
コマンド `git status` を実行すると、自分が origin に対して何コミット分作業を進めたのか表示されます。
```sh
(my-branch)$ git status
# On branch my-branch
# Your branch is ahead of 'origin/my-branch' by 2 commits.
# (use "git push" to publish your local commits)
#
```
origin と同じ状態にリセットする(リモートと同じ状態にする)方法の一つは次の通りです。
```sh
(my-branch)$ git reset --hard origin/my-branch
```
### 新しいブランチではなくマスターブランチにコミットしてしまった
マスターブランチにいたまま、新しいブランチを作成してください。
```sh
(main)$ git branch my-branch
```
マスターブランチを直前のコミットにリセットします。
```sh
(main)$ git reset --hard HEAD^
```
ここで `HEAD^` は `HEAD^1` の短縮形で、`HEAD` の第一の親を表します。
同様に `HEAD^2` は第二の親です(マージには親が二つあります)。
なお、`HEAD^2` は `HEAD~2` と**異なる**ことに注意してください(詳しくは[このリンク](http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde)を参照してください)。
あるいは `HEAD^` を使いたくなければ、マスターブランチを差し戻したい先のコミットハッシュを探し(`git log` を使うとよいです)、そのハッシュにリセットします。
あとは `git push` すればリモートに反映されるはずです。
例えば、マスターブランチを差し戻したいコミットのハッシュが `a13b85e` なら、次のようにします。
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
作業に戻るため、新しいブランチにチェックアウトしましょう。
```sh
(main)$ git checkout my-branch
```
### ファイル全てをリファレンス的な場所に保存しておきたい
ワーキングスパイク(メモを参照)にたくさん編集があって、すべてうまく機能しているものとします。
この作業内容を保存しておくため、別のブランチにコミットします。
```sh
(solution)$ git add -A && git commit -m "Adding all changes from this spike into one big commit."
```
この内容をブランチ(フィーチャーブランチでも `develop` でも)に適用する際は、ファイル全部を保存しておきたいはずです。
大きなコミットを小さなコミットに分割します。
いま、次のブランチがあるものとします。
* `solution` ブランチ。スパイクを解消するためのブランチで、`develop` ブランチに対して一コミット分進んでいます。
* `develop` ブランチ。ここに編集を適用したいとします。
これは編集をブランチに適用することで可能です。
```sh
(develop)$ git checkout solution -- file1.txt
```
これで `solution` ブランチの内容が `develop` ブランチに適用されます。
```sh
# On branch develop
# Your branch is up-to-date with 'origin/develop'.
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: file1.txt
```
あとは通常通りコミットしてください。
メモ:スパイクは問題を解析したり解決するためのものです。
解決法は判断にかけられたあと、共同開発者が問題を理解した時点で破棄されます。~ [Wikipedia](https://en.wikipedia.org/wiki/Extreme_programming_practices)
### 別々のブランチにするべき複数のコミットを一つのブランチにしてしまった
マスターブランチにいるとして、`git log` でコミットが二つ表示されるとします。
```sh
(main)$ git log
commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee
Date: Tue Jul 22 15:39:27 2014 -0400
Bug #21 - Added CSRF protection
commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee
Date: Tue Jul 22 15:39:12 2014 -0400
Bug #14 - Fixed spacing on title
commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose
Date: Tue Jul 21 01:12:48 2014 -0400
First commit
```
それぞれのバグに対応するコミットハッシュをメモしておきます(#21 は`e3851e8`、#14 は`5ea5173` です)。
まず、次のようにしてマスターブランチをあるべきコミット `a13b85e` までリセットします。
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
これで、バグ #21 に対応する新しいブランチを作成できます。
```sh
(main)$ git checkout -b 21
(21)$
```
さて、このブランチにコミットを**チェリーピック**しましょう。
つまり、head が何であろうとそこに当該コミットだけを適用します。
```sh
(21)$ git cherry-pick e3851e8
```
この時点で、コミットのコンフリクトが発生しているかもしれません。
コンフリクトを解消する方法は、[対話的 rebase 章上](#interactive-rebase) セクションの [**コンフリクトがあった**](#merge-conflict) を参照してください。
次に、#14 に対応する、マスターに紐づいたブランチを作成しましょう。
```sh
(21)$ git checkout main
(main)$ git checkout -b 14
(14)$
```
最後に、バグ #14 に対応するコミットをチェリーピックします。
```sh
(14)$ git cherry-pick 5ea5173
```
### upstream で削除されたローカルブランチを削除したい
GitHub でプルリクエストをマージすると、マージされたブランチを自分のフォークから削除する選択肢が出てきます。
そのブランチで今後作業するつもりがなければ、もはや使わないブランチで作業環境が散らからないように削除しておくほうが綺麗です。
```sh
$ git fetch -p upstream
```
ここで `upstream` は取得したい元のリモートを指します。
### 間違ってブランチを削除してしまった
いつもリモートにプッシュしているなら大抵大丈夫です。
ブランチを間違って削除してしまうのはよくあることです。
新しくブランチを作り、ファイルを新規作成したとします。
```sh
(main)$ git checkout -b my-branch
(my-branch)$ git branch
(my-branch)$ touch foo.txt
(my-branch)$ ls
README.md foo.txt
```
これを追加してコミットします。
```sh
(my-branch)$ git add .
(my-branch)$ git commit -m 'foo.txt added'
(my-branch)$ foo.txt added
1 files changed, 1 insertions(+)
create mode 100644 foo.txt
(my-branch)$ git log
commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
Author: siemiatj
Date: Wed Jul 30 00:34:10 2014 +0200
foo.txt added
commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson
Date: Tue Jul 29 13:14:46 2014 -0400
Fixes #6: Force pushing after amending commits
```
マスターに戻って、「間違って」ブランチを削除してみます。
```sh
(my-branch)$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
(main)$ git branch -D my-branch
Deleted branch my-branch (was 4e3cd85).
(main)$ echo oh noes, deleted my branch!
oh noes, deleted my branch!
```
さて、ここで改良されたロガー `reflog` について学びましょう。
これはリポジトリの全ての操作履歴を保存しています。
```
(main)$ git reflog
69204cd HEAD@{0}: checkout: moving from my-branch to main
4e3cd85 HEAD@{1}: commit: foo.txt added
69204cd HEAD@{2}: checkout: moving from main to my-branch
```
このように、削除してしまったブランチのコミットが表示されています。
削除したブランチを復元してみましょう。
```sh
(main)$ git checkout -b my-branch-help
Switched to a new branch 'my-branch-help'
(my-branch-help)$ git reset --hard 4e3cd85
HEAD is now at 4e3cd85 foo.txt added
(my-branch-help)$ ls
README.md foo.txt
```
やった! 消えたファイルを取り戻しました。
コマンド `git reflog` は、リベースが滅茶苦茶になってしまったときにも便利です。
### ブランチを削除したい
リモートブランチを削除するには次を実行します。
```sh
(main)$ git push origin --delete my-branch
```
次のようにもできます。
```sh
(main)$ git push origin :my-branch
```
ローカルブランチを削除するには次の通りです。
```sh
(main)$ git branch -d my-branch
```
現在のブランチか upstream にマージ**されていない**ブランチを削除するには次のようにします。
```sh
(main)$ git branch -D my-branch
```
### 複数のブランチを削除したい
名前が `fix/` で始まるブランチを全て削除したいときは次の通りです。
```sh
(main)$ git branch | grep 'fix/' | xargs git branch -d
```
### ブランチの名前を変更したい
現在の(ローカル)ブランチの名前を変更するには次を実行します。
```sh
(main)$ git branch -m new-name
```
現在いるブランチと異なる(ローカル)ブランチの名前を変更するには次のようにします。
```sh
(main)$ git branch -m old-name new-name
```
古い名前(`old-name`)のリモートブランチを削除し、新しい名前(`new-name`)のブランチをプッシュするには次の通りです。
```sh
(main)$ git push origin :old_name new_name
```
### 他の人が作業しているリモートブランチにチェックアウトしたい
まず、リモートから全ブランチを取得します。
```sh
(main)$ git fetch --all
```
リモートブランチ `daves` にチェックアウトしたいとします。
```sh
(main)$ git checkout --track origin/daves
Branch daves set up to track remote branch daves from origin.
Switched to a new branch 'daves'
```
(ここで `--track` は `git checkout -b [branch] [remotename]/[branch]` の短縮形です。)
これでローカルにブランチ `daves` のコピーが作成され、プッシュした編集がリモートに反映されるようになります。
### 現在のローカルブランチをもとに新しいリモートブランチを作成したい
```sh
$ git push HEAD
```
同時にこのリモートブランチを現在のブランチの upstream に設定したい場合は、代わりに次を実行します。
```sh
$ git push -u HEAD
```
`push.default` の設定が `upstream` モードか `simple` モード(Git 2.0 のデフォルト)になっている場合、次を実行すると、以前に `-u` で登録したリモートブランチに現在のブランチをプッシュします。
```sh
$ git push
```
他のモードが `git push` でどう振る舞うかは、[`push.default` のドキュメント](https://git-scm.com/docs/git-config#Documentation/git-config.txt-pushdefault)で説明されています。
### リモートブランチをローカルブランチの upstream に設定したい
次のようにして、リモートブランチを現在いるローカルブランチの upstream に設定できます。
```sh
$ git branch --set-upstream-to [remotename]/[branch]
# あるいは、短縮形を使う
$ git branch -u [remotename]/[branch]
```
別のローカルブランチの upstream に設定するには次のようにします。
```sh
$ git branch -u [remotename]/[branch] [local-branch]
```
### 自分の HEAD をデフォルトのリモートブランチを追跡するよう設定したい
リモートブランチを調べると、自分の HEAD がどのリモートブランチを追跡しているかがわかります。
ときどきこれが追跡したいブランチと異なることがあります。
```sh
$ git branch -r
origin/HEAD -> origin/gh-pages
origin/main
```
`origin/HEAD` が `origin/main` を追跡するよう設定し直すには、次を実行します。
```sh
$ git remote set-head origin --auto
origin/HEAD set to main
```
### 間違ったブランチを編集してしまった
まだコミットしていない編集を加えたあと、間違ったブランチにいることに気づいたとします。
編集をスタッシュして、適切なブランチに適用すれば大丈夫です。
```sh
(wrong_branch)$ git stash
(wrong_branch)$ git checkout
(correct_branch)$ git stash apply
```
## リベースとマージ
### リベースやマージを取り消したい
現在のブランチを間違ったブランチにリベースないしマージしてしまった、あるいはリベースないしマージができなさそうだと気づいたとしましょう。
Git は危険な操作の前に HEAD が指すものを変数 `ORIG_HEAD` に保存しているので、ブランチをリベースないしマージの前の状態に差し戻すのは簡単です。
```sh
(my-branch)$ git reset --hard ORIG_HEAD
```
### リベースしたが、強制プッシュはしたくない
残念ながら、編集をリモートブランチに反映させるには強制プッシュをする必要があります。
編集履歴を変えてしまったからです。
強制プッシュしない限り、リモートブランチは編集を受け付けません。
これが多くの人がリベースワークフローではなくマージワークフローを使う主な理由です。
特に大規模な開発チームは強制プッシュでハマりやすいです。
リベースの強制プッシュは注意して使いましょう。
リベースの安全な使い方は、リモートには編集を反映させずに、代わりに次を実行することです。
```sh
(main)$ git checkout my-branch
(my-branch)$ git rebase -i main
(my-branch)$ git checkout main
(main)$ git merge --ff-only my-branch
```
詳しくは[この StackOverflow スレッド](https://stackoverflow.com/questions/11058312/how-can-i-use-git-rebase-without-requiring-a-forced-push)を参照してください。
### コミットを統合したい
`main` ブランチにプルリクエストを送る、あるいはこれから送るつもりのブランチで作業しているとします。
最も単純なケースとして、タイムスタンプを気にせずコミット**全部**を一つにまとめたいとします。
この場合はリセットと再コミットを行います。
マスターブランチが最新版で、編集がすべてコミットされていることを確認した上で、次を実行してください。
```sh
(my-branch)$ git reset --soft main
(my-branch)$ git commit -am "New awesome feature"
```
もっと細かく設定し、タイムスタンプも残したい場合は、対話的リベースを使います。
```sh
(my-branch)$ git rebase -i main
```
別のブランチで作業しているわけではない場合、`HEAD` に対してリベースする必要があります。
たとえば直近二件のコミットを圧縮 (squash) したい場合は `HEAD~2`、直近三件なら `HEAD~3` です。
```sh
(main)$ git rebase -i HEAD~2
```
対話的リベースのコマンドを実行したら、テキストエディタに次のように表示されます。
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
pick b729ad5 fixup
pick e3851e8 another fix
# Rebase 8074d12..b729ad5 onto 8074d12
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```
ここで `#` から始まる行はコメントなので、リベースに影響しません。
コマンド `pick` をリストの好きなコマンドで書きかえればよいです。
行を削除すればコミットを削除できます。
例えば、**一番古い(一番目の)コミットはそのまま残し、他のコミット全てを二番目のコミットに統合したい**場合は、最初と二番目以外のコミットの横の文字を `f` に書きかえます。
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
f b729ad5 fixup
f e3851e8 another fix
```
コミットを統合し、**さらに名前も変更したい**場合は、二番目のコミットの横にさらに `r` の文字を追加するか、あるいは単に `f` の代わりに `s` を使います。
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
s b729ad5 fixup
s e3851e8 another fix
```
するとテキストエディタが起動し、コミットの名前を変更できます。
```vim
Newer, awesomer features
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 8074d12
# You are currently editing a commit while rebasing branch 'main' on '8074d12'.
#
# Changes to be committed:
# modified: README.md
#
```
うまくいくと次のように表示されるはずです。
```sh
(main)$ Successfully rebased and updated refs/heads/main.
```
#### 安全なマージの方法
オプション `--no-commit` を指定すると、マージを実行しつつ、あたかもマージが失敗したかのように扱って自動コミットはしません。
これにより、コミットの前にマージの結果を精査したり調整できます。
オプション `--no-ff` はフィーチャーブランチが存在したことを記録に残しておき、プロジェクト履歴の一貫性を保ちます。
```sh
(main)$ git merge --no-ff --no-commit my-branch
```
#### ブランチを一つのコミットにまとめたい場合
```sh
(main)$ git merge --squash my-branch
```
#### プッシュされていないコミットのみを統合したい場合
進行中の作業に関するコミットがいくつかあって、upstream にコミットする前に統合しておきたいとします。
すでに upstream にプッシュされたコミットは、誰かがそれを参照するコミットをしている可能性があるので、それは統合しないでおきたいとします。
```sh
(main)$ git rebase -i @{u}
```
上を実行すると対話的リベースが始まりますが、一覧にはまだプッシュされていないコミットだけが表示されます。
これで順番を入れ替えたり、修正したり、圧縮 (squash) したりしても安全です。
#### マージを中止したい
マージがファイルに問題をきたすことがあります。
こういうときはオプション `abort` を使うとコンフリクト解消の作業を中止し、マージの前の状態の復元を試みることができます。
```sh
(my-branch)$ git merge --abort
```
ただし、このコマンドが使えるのはバージョン 1.7.4 以上の Git に限ります。
### ブランチの親コミットを更新したい
マスターブランチとそこから分岐した feature-1 ブランチがあり、feature-1 からさらに分岐した feature-2 ブランチがあるとします。
いま feature-1 ブランチにコミットしたとすると、feature-2 ブランチの親コミットはもはや正確ではありません(feature-1 から分岐したので、親コミットは feature-1 ブランチの head であるべきです。)
こういうときは `git rebase --onto` で修正できます。
```sh
(feature-2)$ git rebase --onto feature-1 feature-2
```
まだマージされていないブランチからフィーチャーブランチを分岐させており、feature-1 ブランチのバグ修正を feature-2 に反映させたいときに便利です。
### ブランチの全コミットがマージされているか確認する
ブランチの全コミットが別のブランチにマージされているか確認するには、それぞれのブランチの head(あるいは任意のコミット)の間の差分を表示します。
```sh
(main)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
```
一方のブランチにしかないコミットがあるか表示され、ブランチ間で共有されていないコミットの一覧がわかります。
もう一つの方法は次の通りです。
```sh
(main)$ git log main ^feature/120-on-scroll --no-merges
```
### 対話的リベースで起こりうる問題
#### リベース編集画面に 'noop' と表示される
次のように表示された場合です。
```
noop
```
これは、同じコミットのブランチ、あるいは現在のブランチよりも*先*にあるブランチに対してリベースしようとしたときに表示されます。
この場合は、
* マスターブランチが正しい場所にあることを確認してください。
* `HEAD~2` あるいはより以前にリベースしてください。
#### コンフリクトがあった
リベースができないときは、解消すべきコンフリクトがあるかもしれません。
まず `git status` で、どのファイルがコンフリクトを起こしているか確認します。
```sh
(my-branch)$ git status
On branch my-branch
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
both modified: README.md
```
この例では `README.md` にコンフリクトがあります。
ファイルを開き、次のようになっている箇所を見てみましょう。
```vim
<<<<<<< HEAD
some code
=========
some code
>>>>>>> new-commit
```
`HEAD` と新しいコミットで加えられたコードの間の差分(この例では、真ん中の行から `new-commit` の間にあるコード)を解消する必要があります。
一方のブランチの版のコードを残したい場合は、`--ours` あるいは `--theirs` を指定します。
```sh
(main*)$ git checkout --ours README.md
```
- *マージする*場合、ローカルブランチの編集を残したいとき `--ours` を指定し、他方の編集を残したいとき `--theirs` を指定します。
- *リベースする*場合、ローカルブランチの編集を残したいとき `--theirs` を指定し、他方の編集を残したいとき `--ours` を指定します。このように逆転する理由は[ Git ドキュメントのこのノート](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---merge)を参照してください。
マージがもっと複雑なときは、ビジュアル差分エディタを使うとよいです。
```sh
(main*)$ git mergetool -t opendiff
```
コンフリクトを全て解消し、コードのテストが済んだら、`git add ` で編集をステージし、`git rebase --continue` でリベースを再開します。
```sh
(my-branch)$ git add README.md
(my-branch)$ git rebase --continue
```
コンフリクトを解消した結果、ワーキングツリーがコミット前と全く同じ状態になった場合は、代わりに `git rebase --skip` を実行します。
リベース作業を全て中止し、ブランチを元の状態に差し戻したい場合は、次を実行します。
```sh
(my-branch)$ git rebase --abort
```
## スタッシュ
### 全ての編集をスタッシュしたい
ワーキングディレクトリの編集を全てスタッシュするには、次を実行します。
```sh
$ git stash
```
バージョン管理されていないファイルもスタッシュしたいときは、オプション `-u` を指定します。
```sh
$ git stash -u
```
### 特定のファイルをスタッシュしたい
ワーキングディレクトリのファイル一つをスタッシュするには、次を実行します。
```sh
$ git stash push working-directory-path/filename.ext
```
ワーキングディレクトリの複数のファイルをスタッシュするときは次の通りです。
```sh
$ git stash push working-directory-path/filename1.ext working-directory-path/filename2.ext
```
### メッセージをつけてスタッシュしたい
```sh
$ git stash save
```
あるいは次の通りです。
```sh
$ git stash push -m
```
### 一覧から特定のスタッシュを選んで適用したい
まず、次のようにしてスタッシュの一覧をメッセージとともに表示します。
```sh
$ git stash list
```
そして、次のようにして特定のスタッシュを選び適用します。
```sh
$ git stash apply "stash@{n}"
```
ここで、`n` は一覧の中のスタッシュの位置を指します。一番上のスタッシュなら 0 番です。
また、時刻からスタッシュを指定することもできます。
```sh
$ git stash apply "stash@{2.hours.ago}"
```
### ステージされていない編集をそのままにしつつ、スタッシュしたい
手動で `stash commit` を作成し、`git stash store` を実行すればよいです。
```sh
$ git stash create
$ git stash store -m CREATED_SHA1
```
## 検索
### 全コミットから文字列を検索したい
どこかのコミットで導入された文字列を検索したいときは、次のコマンドを使います。
```sh
$ git log -S "string to find"
```
よく使われるパラメータは次の通りです。
* `--source` はコマンドラインでつけられた各コミットの参照名を表示します。
* `--all` は全てのブランチから検索します。
* `--reverse` は逆順に表示します。すなわち最初のコミットから表示します。
### author または committer から検索する
全てのコミットを author または committer の名前から検索するには次のようにします。
```sh
$ git log --author=
$ git log --committer=
```
なお、author と committer は異なることに注意してください。
`--author` ははじめにコードを書いた人、`--committer` は author の代わりにコミットした人を指します。
### 特定のファイルを含むコミットの一覧を表示したい
特定のファイルを含むコミットの一覧を表示するには、次を実行します。
```sh
$ git log --
```
通常は正確なパスを指定しますが、パスやファイル名にワイルドカードを使うこともできます。
```sh
$ git log -- **/*.js
```
ワイルドカードを使う際は、`--name-status` を指定すると、コミットされたファイルの一覧が表示されて便利です。
```sh
$ git log --name-status -- **/*.js
```
### 特定の関数に関するコミット履歴を見たい
特定の関数の履歴を追跡するには次を実行します。
```sh
$ git log -L :FunctionName:FilePath
```
このコマンドは `git log` の他のオプション、例えば [revision ranges](https://git-scm.com/docs/gitrevisions) や [commit limits](https://git-scm.com/docs/git-log/#_commit_limiting) と一緒に使うことができます。
### コミットが参照されているタグを検索したい
特定のコミットを含むタグを検索するには次のようにします。
```sh
$ git tag --contains
```
## サブモジュール
### 全てのサブモジュールをクローンする
```sh
$ git clone --recursive git://github.com/foo/bar.git
```
すでにクローンしている場合は次の通りです。
```sh
$ git submodule update --init --recursive
```
### サブモジュールを削除する
サブモジュールの作成はきわめて簡単ですが、削除はそうでもありません。
削除に必要なコマンドは次の通りです。
```sh
$ git submodule deinit submodulename
$ git rm submodulename
$ git rm --cached submodulename
$ rm -rf .git/modules/submodulename
```
## その他色々
### あるブランチから別のブランチにフォルダをコピーしたい
```sh
$ git checkout --
```
### 削除されたファイルを復元したい
まず、ファイルが最後に存在していたコミットを探します。
```sh
$ git rev-list -n 1 HEAD -- filename
```
見つけたら、ファイルをチェックアウトします。
```
git checkout deletingcommitid^ -- filename
```
### タグを削除したい
```sh
$ git tag -d
$ git push :refs/tags/
```
### 削除されたタグを復元したい
削除されたタグを復元する手順は次の通りです。
まず、unreachable になったタグを探します。
```sh
$ git fsck --unreachable | grep tag
```
タグのハッシュをメモしておきます。
続いて、次のように [`git update-ref`](https://git-scm.com/docs/git-update-ref) で削除されたタグを復元します。
```sh
$ git update-ref refs/tags/
```
これでタグが復元されたはずです。
### 削除されたパッチを取得したい
誰かが GitHub でプルリクエストを送ったあとにフォークを削除してしまった場合、そのリポジトリをクローンしたり、`git am` でパッチを適用することができなくなります。
[.diff や .patch](https://github.com/blog/967-github-secrets) の URL が使えなくなってしまうためです。
しかし、[GitHub 独自の参照](https://gist.github.com/piscisaureus/3342247)を使って、プルリクエスト自体をチェックアウトすることができます。
プルリクエスト #1 の内容を新しいブランチ pr_1 に取得するには、次を実行します。
```sh
$ git fetch origin refs/pull/1/head:pr_1
From github.com:foo/bar
* [new ref] refs/pull/1/head -> pr_1
```
### リポジトリを zip ファイルとしてエクスポートしたい
```sh
$ git archive --format zip --output /full/path/to/zipfile.zip main
```
### 同じ名前のブランチとタグをプッシュしたい
ブランチと同じ名前のタグがリモートリポジトリに存在する場合、通常通り `git push ` でプッシュしようとすると、次のようなエラーが出ます。
```sh
$ git push origin
error: dst refspec same matches more than one.
error: failed to push some refs to ''
```
このエラーはブランチのヘッドを指定することで回避できます。
```sh
$ git push origin refs/heads/
```
同名のブランチがリモートリポジトリにあるタグをプッシュしたいときも、似たコマンドを使います。
```sh
$ git push origin refs/tags/
```
## ファイルの追跡
### ファイルの内容は変えずに、ファイル名の大文字・小文字を変更したい
```sh
(main)$ git mv --force myfile MyFile
```
### git pull してローカルのファイルを上書きしたい
```sh
(main)$ git fetch --all
(main)$ git reset --hard origin/main
```
### ファイルを残しつつ Git から削除したい
```sh
(main)$ git rm --cached log.txt
```
### ファイルを特定の版まで差し戻したい
差し戻したいコミットのハッシュが `c5f567` なら、次を実行します。
```sh
(main)$ git checkout c5f567 -- file1/to/restore file2/to/restore
```
差し戻したいコミットが c5f567 の一つ前なら、コミットハッシュに `c5f567~1` を指定します。
```sh
(main)$ git checkout c5f567~1 -- file1/to/restore file2/to/restore
```
### 特定のファイルのコミット間・ブランチ間の差分を表示したい
コミット c5f567 とその一つ前の間の差分を表示したい場合、次を実行します。
```sh
$ git diff HEAD:path_to_file/file c5f567:path_to_file/file
# または
$ git diff HEAD c5f567 -- path_to_file/file
```
ブランチでも同様です。
```sh
$ git diff main:path_to_file/file staging:path_to_file/file
# または
$ git diff main staging -- path_to_file/file
```
### 特定のファイルの変更を無視したい
これはローカル環境で設定テンプレートにコミットできない認証情報を追加する必要があるときなどに役立ちます。
```sh
$ git update-index --assume-unchanged file-to-ignore
```
ファイルがバージョン管理されなくなるわけでは*ない*ことに注意してください。ローカルで無視されるだけです。
設定を取り消して変更を再び追跡するには、次のように ignore フラッグを削除します。
```sh
$ git update-index --no-assume-unchanged file-to-stop-ignoring
```
## Git によるデバッグ
コマンド [git-bisect](https://git-scm.com/docs/git-bisect) は、Git 履歴を二分探索してバグをもたらしたコミットを探します。
いま `main` ブランチにいるとして、失敗をやらかしたコミットを探してみましょう。
次のコマンドで二分探索を始めます。
```sh
$ git bisect start
```
問題のあるコミットとないコミットを指定する必要があります。
*現在の*バージョンに問題があり、`v1.1.1` は問題ないとします。
```sh
$ git bisect bad
$ git bisect good v1.1.1
```
すると、`git-bisect` は選んだバージョンの中間のコミットを選んで調べ、問題があるかどうか尋ねてきます。
次のように表示されるはずです。
```sh
$ Bisecting: 5 revision left to test after this (roughly 5 step)
$ [c44abbbee29cb93d8499283101fe7c8d9d97f0fe] Commit message
$ (c44abbb)$
```
このコミットに問題があるか調べましょう。
問題がない (good) 場合は次を実行します。
```sh
$ (c44abbb)$ git bisect good
```
すると、`git-bisect` はまた別のコミットを選択します。
このように `good` か `bad` を選んでいく作業は、調べるコミットがなくなるまで続きます。
終了したら、コマンドラインには問題をきたした**最初の**コミットの詳細が表示されます。
## 設定
### Git コマンドにエイリアスを設定したい
OS X と Linux では、Git 設定ファイルは ```~/.gitconfig``` に保存されています。
私の場合、ショートカット(とよくやるタイポ)のために次のようなものを ```[alias]``` セクションに設定しています。
```vim
[alias]
a = add
amend = commit --amend
c = commit
ca = commit --amend
ci = commit -a
co = checkout
d = diff
dc = diff --changed
ds = diff --staged
extend = commit --amend -C HEAD
f = fetch
loll = log --graph --decorate --pretty=oneline --abbrev-commit
m = merge
one = log --pretty=oneline
outstanding = rebase -i @{u}
reword = commit --amend --only
s = status
unpushed = log @{u}
wc = whatchanged
wip = rebase -i @{u}
zap = fetch -p
day = log --reverse --no-merges --branches=* --date=local --since=midnight --author=\"$(git config --get user.name)\"
delete-merged-branches = "!f() { git checkout --quiet main && git branch --merged | grep --invert-match '\\*' | xargs -n 1 git branch --delete; git checkout --quiet @{-1}; }; f"
```
### 空のディレクトリをリポジトリに加えたい
できません! Git ではできませんが、ハックする方法があります。
次のような内容の .gitignore を作成してディレクトリに加えればよいです。
```
# Ignore everything in this directory
*
# Except this file
!.gitignore
```
もう一つのよくある方法は、.gitkeep という名前の空のファイルをディレクトリに作成することです。
```sh
$ mkdir mydir
$ touch mydir/.gitkeep
```
単に .keep という名前でもよいです。この場合、二行目は ```touch mydir/.keep``` とします。
### リポジトリへのユーザ名とパスワードをキャッシュしたい
認証が必要なリポジトリがあるとします。
ユーザ名とパスワードをキャッシュしておけば、プッシュやプルのたび入力せずに済みます。
認証情報ヘルパーが役に立ちます。
```sh
$ git config --global credential.helper cache
# Git が認証情報キャッシュを使うよう設定する
```
```sh
$ git config --global credential.helper 'cache --timeout=3600'
# キャッシュが一時間でタイムアウトするよう設定する(設定は秒単位)
```
認証情報ヘルパーを探すには、次を実行します。
```sh
$ git help -a | grep credential
# 認証情報ヘルパーの候補が表示される
```
OS 固有の認証情報キャッシュは次の通りです。
```sh
$ git config --global credential.helper osxkeychain
# OSX
```
```sh
$ git config --global credential.helper manager
# Git for Windows 2.7.3+
```
```sh
$ git config --global credential.helper gnome-keyring
# Ubuntu やその他の GNOME ベースディストリビューション
```
その他のディストリビューションや OS 向けの認証情報キャッシュもあります。
### パーミッションとファイルモードの変更を Git が無視するようにしたい
```sh
$ git config core.fileMode false
```
これをログインユーザ向けのデフォルト設定にしたい場合、次を実行します。
```sh
$ git config --global core.fileMode false
```
### グローバルユーザを設定したい
全てのローカルリポジトリにわたるユーザ情報を設定し、バージョン履歴のレビューの際にわかりやすい名前を設定するには、次のようにします。
```sh
$ git config --global user.name “[firstname lastname]”
```
各履歴のマーカーに紐づけられるメールアドレスを設定したい場合は次の通りです。
```sh
git config --global user.email “[valid-email]”
```
## 何を間違ったかわからないとき
何かやらかした場合です。つまり、何かを `reset` してしまった、間違ったブランチをマージしてしまった、あるいは強制プッシュしてしまいコミットが見つけられない、といった状況です。
ある時点まではうまくいっていたので、その状態に戻したいとします。
こうしたときに `git reflog` が役に立ちます。
`reflog` は、ブランチが他のブランチやタグに参照されていなくても、ブランチになされた変更を記録しています。
HEAD が変更される際は基本的に reflog に記録が追加されます。
ただ、残念ながら機能するのはローカルリポジトリのみで、記録するのは変化だけです(たとえば、どこにも記録されていないファイルへの変更は記録されません)。
```sh
(main)$ git reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
```
上の reflog には、main から 2.2 へのチェックアウトが表示されています。
それから古いコミットへの hard reset があります。
最新のアクティビティは一番上に `HEAD@{0}` のラベルで表示されます。
間違えて差し戻ししてしまったとします。
コミット二つを間違って捨ててしまう前の、`0254ea7` を参照するコミットを reflog は保持しています。
```sh
$ git reset --hard 0254ea7
```
コマンド `git reset` を使って、マスターブランチを以前の状態に戻すことができます。
履歴を間違えて変更してしまった場合の安全策です。
([出典](https://www.atlassian.com/git/tutorials/rewriting-history/git-reflog) からコピー・改変しました。)
## Git ショートカット
### Git Bash
上記のコマンドに慣れてきたら、Git Bash のショートカットを作りたくなるはずです。
複雑なタスクを短いコマンドで素早く行うことができるようになります。
```sh
alias sq=squash
function squash() {
git rebase -i HEAD~$1
}
```
このコマンドを .bashrc か .bash_profile にコピーしてください。
### Windows の PowerShell
Windows で PowerShell を使っているなら、エイリアスや関数を作成できます。profile に次のコマンドを追加してください。
profile のパスは `$profile` に定義されています。詳しくは Microsoft のドキュメントサイトの [About Profiles](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles) を参照してください。
```powershell
Set-Alias sq Squash-Commits
function Squash-Commits {
git rebase -i HEAD~$1
}
```
# 文献
## 本
* [Learn Enough Git to Be Dangerous](https://www.learnenough.com/git-tutorial) - Git の基礎からカバーした Michael Hartl の本
* [Pro Git](https://git-scm.com/book/en/v2) - Scott Chacon と Ben Straub による Git に関する素晴らしい本
* [Git Internals](https://github.com/pluralsight/git-internals-pdf) - Scott Chacon による Git に関する素晴らしい本
* [Nasa handbook](https://www.nasa.gov/sites/default/files/atoms/files/nasa_systems_engineering_handbook.pdf)
## チュートリアル
* [19 Git Tips For Everyday Use](https://www.alexkras.com/19-git-tips-for-everyday-use) - Git の便利なワンライナーの一覧
* [Atlassian's Git tutorial](https://www.atlassian.com/git/tutorials) - 初心者から上級者までチュートリアルで Git を正しく使おう
* [Learn Git branching](https://learngitbranching.js.org/) - web で動くブランチ・マージ・リベースの対話的なチュートリアル
* [Getting solid at Git rebase vs. merge](https://medium.com/@porteneuve/getting-solid-at-git-rebase-vs-merge-4fa1a48c53aa)
* [Git Commands and Best Practices Cheat Sheet](https://zeroturnaround.com/rebellabs/git-commands-and-best-practices-cheat-sheet) - ブログ投稿にあるもっと説明の豊富な Git チートシート
* [Git from the inside out](https://codewords.recurse.com/issues/two/git-from-the-inside-out) - Git の内部に踏み込んだチュートリアル
* [git-workflow](https://github.com/asmeurer/git-workflow) - [Aaron Meurer](https://github.com/asmeurer) による Git を使ってオープンソースリポジトリに貢献する方法の解説
* [GitHub as a workflow](https://hugogiraudel.com/2015/08/13/github-as-a-workflow/) - GitHub を、特に空のプルリクエストを用いてワークフローとして使う面白い試み
* [Githug](https://github.com/Gazler/githug) - よく使う Git ワークフローを学ぶゲーム
* [learnGitBranching](https://github.com/pcottle/learnGitBranching) - 刺激を得たり教育に使える git の可視化
## スクリプトとツール
* [firstaidgit.io](http://firstaidgit.io/) - 検索可能な Git のよくある質問集
* [git-extra-commands](https://github.com/unixorn/git-extra-commands) - 便利な Git スクリプトのコレクション
* [git-extras](https://github.com/tj/git-extras) - Git の 便利機能 -- リポジトリの要約、REPL、編集履歴の人口、開発者ごとのコミット率など
* [git-fire](https://github.com/qw3rtman/git-fire) - 緊急時に(マージのコンフリクトを防ぐため)新しいブランチに全ファイルを追加・コミット・プッシュする Git のプラグイン
* [git-tips](https://github.com/git-tips/tips) - ちょっとした Git のコツ
* [git-town](https://github.com/Originate/git-town) - 一般的でハイレベルな Git ワークフローのサポート http://www.git-town.com
## GUI クライアント
* [GitKraken](https://www.gitkraken.com/) - Windows, Mac, Linux 向けの真正で豪華な Git クライアント
* [git-cola](https://git-cola.github.io/) - Windows と OS X 向けのもう一つの Git クライアント
* [GitUp](https://github.com/git-up/GitUp) - Git の問題に対処する志向の強い方法をそなえた新しい GUI クライアント
* [gitx-dev](https://rowanj.github.io/gitx/) - OS X 向けのグラフィカル Git クライアント
* [Sourcetree](https://www.sourcetreeapp.com/) - シンプルさと強力さを兼ね備えた Windows と Mac 向けの美しい Git GUI です。で動きます。
* [Tower](https://www.git-tower.com/) - OS X で動くグラフィカル Git クライアント(有料)
* [tig](https://jonas.github.io/tig/) - Git のターミナルテキストモードインターフェース
* [Magit](https://magit.vc/) - Emacs のパッケージとして実装された Git のインターフェース
* [GitExtensions](https://github.com/gitextensions/gitextensions) - シェル拡張・Visual Studio 2010-2015 プラグイン・スタンドアローンの Git リポジトリツール
* [Fork](https://git-fork.com/) - Mac で動く高速で使いやすい Git クライアント(ベータ版)
* [gmaster](https://gmaster.io/) - Windows で動く、三者間マージ、リファクタリング解析、セマンティック差分とマージのできる Git クライアント(ベータ版)
* [gitk](https://git-scm.com/docs/gitk) - Linux 向けのリポジトリの状態をシンプルに見られる Git クライアント
* [SublimeMerge](https://www.sublimemerge.com/) - 三者間マージ・強力な検索機能・シンタックスハイライトをそなえた驚異的に高速で拡張性の高いクライアント。活発に開発されている
================================================
FILE: README_kr.md
================================================
# 깃을 위한 flight rules
🌍
*[English](README.md) ∙ [Español](README_es.md) ∙ [Русский](README_ru.md) ∙ [繁體中文](README_zh-TW.md) ∙ [简体中文](README_zh-CN.md) ∙ [한국어](README_kr.md) ∙ [Tiếng Việt](README_vi.md) ∙ [Français](README_fr.md) ∙ [日本語](README_ja.md)*
#### flight rules 가 뭐야?
뭔가 잘못 됐을 때 뭘 해야할지에 대한 우주비행사를 위한 가이드 (여기선 깃을 쓰는 개발자를 위한)
> *Flight Rules* 는 어떤 문제 X가 발생한 이유와 그 단계의 매뉴얼에서 어렵사리 얻은 지식이에요. 기본적으로 각 시나리오의 매우 자세하고 구체적인 운영 절차랍니다. [...]
> NASA는 수성(Mercury) 시대 때 지상팀에서 처음으로 "lessons learned" 이란 것을 모았는데 수천개의 문제의 상황들, 부서진 해치 손잡이로 인한 엔진 고장부터 컴퓨터 문제 그리고 그 해답까지, 1960년대 초부터 우리의 실수들, 재앙들, 해결책 등이 목록화 돼있어요.
— Chris Hadfield, *인생을 위한 우주비행사의 가이드*.
#### 이 문서의 규칙
명확하게 하기 위해 이 문서의 모든 예제는 현재 브랜치를 표시하고 스테이지에 변경이 있는지를 나타내기 위해 커스텀 된 배시 프롬프트를 써요. 브랜치는 괄호 안에 있고, 브랜치 다음의 *는 스테이지의 변경된 것을 나타내요.
[](https://gitter.im/k88hudson/git-flight-rules?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [레파지토리](#%EB%A0%88%ED%8C%8C%EC%A7%80%ED%86%A0%EB%A6%AC)
- [로컬 레파지토리에서 시작하고 싶어](#%EB%A1%9C%EC%BB%AC-%EB%A0%88%ED%8C%8C%EC%A7%80%ED%86%A0%EB%A6%AC%EC%97%90%EC%84%9C-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [난 리모트 레파지토리를 복제해오고 싶어](#%EB%82%9C-%EB%A6%AC%EB%AA%A8%ED%8A%B8-%EB%A0%88%ED%8C%8C%EC%A7%80%ED%86%A0%EB%A6%AC%EB%A5%BC-%EB%B3%B5%EC%A0%9C%ED%95%B4%EC%98%A4%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [커밋 수정](#%EC%BB%A4%EB%B0%8B-%EC%88%98%EC%A0%95)
- [내가 방금 어떤 커밋을 남겼지?](#%EB%82%B4%EA%B0%80-%EB%B0%A9%EA%B8%88-%EC%96%B4%EB%96%A4-%EC%BB%A4%EB%B0%8B%EC%9D%84-%EB%82%A8%EA%B2%BC%EC%A7%80)
- [커밋 메세지를 잘못 썼어](#%EC%BB%A4%EB%B0%8B-%EB%A9%94%EC%84%B8%EC%A7%80%EB%A5%BC-%EC%9E%98%EB%AA%BB-%EC%8D%BC%EC%96%B4)
- [커밋을 다른 이름과 이메일 설정으로 해버렸어](#%EC%BB%A4%EB%B0%8B%EC%9D%84-%EB%8B%A4%EB%A5%B8-%EC%9D%B4%EB%A6%84%EA%B3%BC-%EC%9D%B4%EB%A9%94%EC%9D%BC-%EC%84%A4%EC%A0%95%EC%9C%BC%EB%A1%9C-%ED%95%B4%EB%B2%84%EB%A0%B8%EC%96%B4)
- [지난 커밋에서 파일 하나를 지우고 싶어](#%EC%A7%80%EB%82%9C-%EC%BB%A4%EB%B0%8B%EC%97%90%EC%84%9C-%ED%8C%8C%EC%9D%BC-%ED%95%98%EB%82%98%EB%A5%BC-%EC%A7%80%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [마지막 커밋을 지우고 싶어](#%EB%A7%88%EC%A7%80%EB%A7%89-%EC%BB%A4%EB%B0%8B%EC%9D%84-%EC%A7%80%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [임의적인 커밋 지우기](#%EC%9E%84%EC%9D%98%EC%A0%81%EC%9D%B8-%EC%BB%A4%EB%B0%8B-%EC%A7%80%EC%9A%B0%EA%B8%B0)
- [수정된 커밋을 푸시했는데, 에러 메세지가 떠](#%EC%88%98%EC%A0%95%EB%90%9C-%EC%BB%A4%EB%B0%8B%EC%9D%84-%ED%91%B8%EC%8B%9C%ED%96%88%EB%8A%94%EB%8D%B0-%EC%97%90%EB%9F%AC-%EB%A9%94%EC%84%B8%EC%A7%80%EA%B0%80-%EB%96%A0)
- [하드 리셋을 해버렸는데 되돌리고 싶어](#%ED%95%98%EB%93%9C-%EB%A6%AC%EC%85%8B%EC%9D%84-%ED%95%B4%EB%B2%84%EB%A0%B8%EB%8A%94%EB%8D%B0-%EB%90%98%EB%8F%8C%EB%A6%AC%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [머지를 실수로 커밋, 푸시해버렸어](#%EB%A8%B8%EC%A7%80%EB%A5%BC-%EC%8B%A4%EC%88%98%EB%A1%9C-%EC%BB%A4%EB%B0%8B-%ED%91%B8%EC%8B%9C%ED%95%B4%EB%B2%84%EB%A0%B8%EC%96%B4)
- [스테이지](#%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80)
- [지난 커밋에 스테이지 변경점을 추가하고 싶어](#%EC%A7%80%EB%82%9C-%EC%BB%A4%EB%B0%8B%EC%97%90-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EB%B3%80%EA%B2%BD%EC%A0%90%EC%9D%84-%EC%B6%94%EA%B0%80%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [전체가 아닌 새 파일만 스테이지에 올리고 싶어](#%EC%A0%84%EC%B2%B4%EA%B0%80-%EC%95%84%EB%8B%8C-%EC%83%88-%ED%8C%8C%EC%9D%BC%EB%A7%8C-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80%EC%97%90-%EC%98%AC%EB%A6%AC%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [하나의 파일 변경점을 두개의 다른 커밋에 남기고 싶어](#%ED%95%98%EB%82%98%EC%9D%98-%ED%8C%8C%EC%9D%BC-%EB%B3%80%EA%B2%BD%EC%A0%90%EC%9D%84-%EB%91%90%EA%B0%9C%EC%9D%98-%EB%8B%A4%EB%A5%B8-%EC%BB%A4%EB%B0%8B%EC%97%90-%EB%82%A8%EA%B8%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [아직 스테이지에 안 올라간 변경점을 스테이지에 추가하고, 스테이지에 있는 변경점을 다시 빼고 싶어](#%EC%95%84%EC%A7%81-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80%EC%97%90-%EC%95%88-%EC%98%AC%EB%9D%BC%EA%B0%84-%EB%B3%80%EA%B2%BD%EC%A0%90%EC%9D%84-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80%EC%97%90-%EC%B6%94%EA%B0%80%ED%95%98%EA%B3%A0-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80%EC%97%90-%EC%9E%88%EB%8A%94-%EB%B3%80%EA%B2%BD%EC%A0%90%EC%9D%84-%EB%8B%A4%EC%8B%9C-%EB%B9%BC%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [스테이지 전의 변경점](#%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EC%A0%84%EC%9D%98-%EB%B3%80%EA%B2%BD%EC%A0%90)
- [스테이지 전의 변경점을 새 브랜치로 옮기고 싶어](#%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EC%A0%84%EC%9D%98-%EB%B3%80%EA%B2%BD%EC%A0%90%EC%9D%84-%EC%83%88-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A1%9C-%EC%98%AE%EA%B8%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [스테이지전 변경점을 만들어둔 다른 브랜치로 옮기고 싶어](#%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80%EC%A0%84-%EB%B3%80%EA%B2%BD%EC%A0%90%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%91%94-%EB%8B%A4%EB%A5%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A1%9C-%EC%98%AE%EA%B8%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [내 로컬에 있는 커밋 안된 변경점을 다 무시하고 싶어 (스테이징 됐던 안됐던)](#%EB%82%B4-%EB%A1%9C%EC%BB%AC%EC%97%90-%EC%9E%88%EB%8A%94-%EC%BB%A4%EB%B0%8B-%EC%95%88%EB%90%9C-%EB%B3%80%EA%B2%BD%EC%A0%90%EC%9D%84-%EB%8B%A4-%EB%AC%B4%EC%8B%9C%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%95-%EB%90%90%EB%8D%98-%EC%95%88%EB%90%90%EB%8D%98)
- [스테이지 안된 특정 변경점을 지우고 싶어](#%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EC%95%88%EB%90%9C-%ED%8A%B9%EC%A0%95-%EB%B3%80%EA%B2%BD%EC%A0%90%EC%9D%84-%EC%A7%80%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [스테이지 안된 특정 파일을 지우고 싶어](#%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EC%95%88%EB%90%9C-%ED%8A%B9%EC%A0%95-%ED%8C%8C%EC%9D%BC%EC%9D%84-%EC%A7%80%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [로컬에 있는 스테이지 안된 변경점만 지우고 싶어](#%EB%A1%9C%EC%BB%AC%EC%97%90-%EC%9E%88%EB%8A%94-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%A7%80-%EC%95%88%EB%90%9C-%EB%B3%80%EA%B2%BD%EC%A0%90%EB%A7%8C-%EC%A7%80%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [트래킹 안된 파일들 다 지우고 싶어](#%ED%8A%B8%EB%9E%98%ED%82%B9-%EC%95%88%EB%90%9C-%ED%8C%8C%EC%9D%BC%EB%93%A4-%EB%8B%A4-%EC%A7%80%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [브랜치](#%EB%B8%8C%EB%9E%9C%EC%B9%98)
- [모든 브랜치 리스트를 보고 싶어](#%EB%AA%A8%EB%93%A0-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EB%B3%B4%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [커밋에서 브랜치 만들기](#%EC%BB%A4%EB%B0%8B%EC%97%90%EC%84%9C-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%A7%8C%EB%93%A4%EA%B8%B0)
- [다른 브랜치에서 풀을 받아와버렸어](#%EB%8B%A4%EB%A5%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%97%90%EC%84%9C-%ED%92%80%EC%9D%84-%EB%B0%9B%EC%95%84%EC%99%80%EB%B2%84%EB%A0%B8%EC%96%B4)
- [로컬의 커밋을 지워서 서버에 있는 내 브랜치와 맞추고 싶어](#%EB%A1%9C%EC%BB%AC%EC%9D%98-%EC%BB%A4%EB%B0%8B%EC%9D%84-%EC%A7%80%EC%9B%8C%EC%84%9C-%EC%84%9C%EB%B2%84%EC%97%90-%EC%9E%88%EB%8A%94-%EB%82%B4-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%99%80-%EB%A7%9E%EC%B6%94%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [새 브랜치 대신에 마스터에 커밋을 해버렸어](#%EC%83%88-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EB%8C%80%EC%8B%A0%EC%97%90-%EB%A7%88%EC%8A%A4%ED%84%B0%EC%97%90-%EC%BB%A4%EB%B0%8B%EC%9D%84-%ED%95%B4%EB%B2%84%EB%A0%B8%EC%96%B4)
- [다른 레퍼런스 같은 곳에서 모든 파일을 유지하고 싶어](#%EB%8B%A4%EB%A5%B8-%EB%A0%88%ED%8D%BC%EB%9F%B0%EC%8A%A4-%EA%B0%99%EC%9D%80-%EA%B3%B3%EC%97%90%EC%84%9C-%EB%AA%A8%EB%93%A0-%ED%8C%8C%EC%9D%BC%EC%9D%84-%EC%9C%A0%EC%A7%80%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [한 브랜치에 다른 브랜치에 남겼어야 하는 커밋을 여러개 남겼어](#%ED%95%9C-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%97%90-%EB%8B%A4%EB%A5%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%97%90-%EB%82%A8%EA%B2%BC%EC%96%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%BB%A4%EB%B0%8B%EC%9D%84-%EC%97%AC%EB%9F%AC%EA%B0%9C-%EB%82%A8%EA%B2%BC%EC%96%B4)
- [업스트림에선 지워진 로컬 브랜치를 지우고 싶어](#%EC%97%85%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%97%90%EC%84%A0-%EC%A7%80%EC%9B%8C%EC%A7%84-%EB%A1%9C%EC%BB%AC-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-%EC%A7%80%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [브랜치를 지워버렸어](#%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-%EC%A7%80%EC%9B%8C%EB%B2%84%EB%A0%B8%EC%96%B4)
- [브랜치를 지우고 싶어](#%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-%EC%A7%80%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [여러개의 브랜치를 지우고 싶어](#%EC%97%AC%EB%9F%AC%EA%B0%9C%EC%9D%98-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-%EC%A7%80%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [브랜치 이름을 바꾸고 싶어](#%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%9D%B4%EB%A6%84%EC%9D%84-%EB%B0%94%EA%BE%B8%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [다른 사람이 작업중인 리모트 브랜치로 체크아웃 하고 싶어](#%EB%8B%A4%EB%A5%B8-%EC%82%AC%EB%9E%8C%EC%9D%B4-%EC%9E%91%EC%97%85%EC%A4%91%EC%9D%B8-%EB%A6%AC%EB%AA%A8%ED%8A%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A1%9C-%EC%B2%B4%ED%81%AC%EC%95%84%EC%9B%83-%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [현재 로컬 브랜치로 새로운 리모트 브랜치를 만들고 싶어](#%ED%98%84%EC%9E%AC-%EB%A1%9C%EC%BB%AC-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A1%9C-%EC%83%88%EB%A1%9C%EC%9A%B4-%EB%A6%AC%EB%AA%A8%ED%8A%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [리모트 브랜치를 로컬 브랜치를 위한 업스트림으로 설정하고 싶어](#%EB%A6%AC%EB%AA%A8%ED%8A%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-%EB%A1%9C%EC%BB%AC-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%97%85%EC%8A%A4%ED%8A%B8%EB%A6%BC%EC%9C%BC%EB%A1%9C-%EC%84%A4%EC%A0%95%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [HEAD를 기본 리모트 브랜치로 트래킹하도록 설정하고 싶어](#head%EB%A5%BC-%EA%B8%B0%EB%B3%B8-%EB%A6%AC%EB%AA%A8%ED%8A%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A1%9C-%ED%8A%B8%EB%9E%98%ED%82%B9%ED%95%98%EB%8F%84%EB%A1%9D-%EC%84%A4%EC%A0%95%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [다른 브랜치에 변경점을 잘못 남겼어](#%EB%8B%A4%EB%A5%B8-%EB%B8%8C%EB%9E%9C%EC%B9%98%EC%97%90-%EB%B3%80%EA%B2%BD%EC%A0%90%EC%9D%84-%EC%9E%98%EB%AA%BB-%EB%82%A8%EA%B2%BC%EC%96%B4)
- [리베이스와 머지](#%EB%A6%AC%EB%B2%A0%EC%9D%B4%EC%8A%A4%EC%99%80-%EB%A8%B8%EC%A7%80)
- [리베이스/머지 한 걸 되돌리고 싶어](#%EB%A6%AC%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A8%B8%EC%A7%80-%ED%95%9C-%EA%B1%B8-%EB%90%98%EB%8F%8C%EB%A6%AC%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [리베이스를 했는데, 강제 푸시하고 싶진 않아](#%EB%A6%AC%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%ED%96%88%EB%8A%94%EB%8D%B0-%EA%B0%95%EC%A0%9C-%ED%91%B8%EC%8B%9C%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%A7%84-%EC%95%8A%EC%95%84)
- [커밋끼리 합치고 싶어](#%EC%BB%A4%EB%B0%8B%EB%81%BC%EB%A6%AC-%ED%95%A9%EC%B9%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [안전한 머지 전략](#%EC%95%88%EC%A0%84%ED%95%9C-%EB%A8%B8%EC%A7%80-%EC%A0%84%EB%9E%B5)
- [브랜치를 커밋 하나로 머지해야해](#%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%A5%BC-%EC%BB%A4%EB%B0%8B-%ED%95%98%EB%82%98%EB%A1%9C-%EB%A8%B8%EC%A7%80%ED%95%B4%EC%95%BC%ED%95%B4)
- [푸시 되지 않은 커밋만 합치고 싶어](#%ED%91%B8%EC%8B%9C-%EB%90%98%EC%A7%80-%EC%95%8A%EC%9D%80-%EC%BB%A4%EB%B0%8B%EB%A7%8C-%ED%95%A9%EC%B9%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [머지를 중단해야해](#%EB%A8%B8%EC%A7%80%EB%A5%BC-%EC%A4%91%EB%8B%A8%ED%95%B4%EC%95%BC%ED%95%B4)
- [브랜치내 모든 커밋이 머지됐는지 확인해](#%EB%B8%8C%EB%9E%9C%EC%B9%98%EB%82%B4-%EB%AA%A8%EB%93%A0-%EC%BB%A4%EB%B0%8B%EC%9D%B4-%EB%A8%B8%EC%A7%80%EB%90%90%EB%8A%94%EC%A7%80-%ED%99%95%EC%9D%B8%ED%95%B4)
- [대화형 리베이스로 발생가능한 이슈](#%EB%8C%80%ED%99%94%ED%98%95-%EB%A6%AC%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A1%9C-%EB%B0%9C%EC%83%9D%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9D%B4%EC%8A%88)
- [리베이스 편집 화면에서 'noop'](#%EB%A6%AC%EB%B2%A0%EC%9D%B4%EC%8A%A4-%ED%8E%B8%EC%A7%91-%ED%99%94%EB%A9%B4%EC%97%90%EC%84%9C-noop)
- [충돌이 있어](#%EC%B6%A9%EB%8F%8C%EC%9D%B4-%EC%9E%88%EC%96%B4)
- [스테이시](#%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%8B%9C)
- [모든 변경점 스테이시 하기](#%EB%AA%A8%EB%93%A0-%EB%B3%80%EA%B2%BD%EC%A0%90-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%8B%9C-%ED%95%98%EA%B8%B0)
- [특정 파일들만 스테이시 하기](#%ED%8A%B9%EC%A0%95-%ED%8C%8C%EC%9D%BC%EB%93%A4%EB%A7%8C-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%8B%9C-%ED%95%98%EA%B8%B0)
- [메세지와 함께 스테이시 하기](#%EB%A9%94%EC%84%B8%EC%A7%80%EC%99%80-%ED%95%A8%EA%BB%98-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%8B%9C-%ED%95%98%EA%B8%B0)
- [특정 스테이시 목록에서 가져와 적용하기](#%ED%8A%B9%EC%A0%95-%EC%8A%A4%ED%85%8C%EC%9D%B4%EC%8B%9C-%EB%AA%A9%EB%A1%9D%EC%97%90%EC%84%9C-%EA%B0%80%EC%A0%B8%EC%99%80-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0)
- [찾아보기](#%EC%B0%BE%EC%95%84%EB%B3%B4%EA%B8%B0)
- [어떤 커밋에서 문자열을 찾고 싶어](#%EC%96%B4%EB%96%A4-%EC%BB%A4%EB%B0%8B%EC%97%90%EC%84%9C-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84-%EC%B0%BE%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [작성자나 커미터를 찾고 싶어](#%EC%9E%91%EC%84%B1%EC%9E%90%EB%82%98-%EC%BB%A4%EB%AF%B8%ED%84%B0%EB%A5%BC-%EC%B0%BE%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [특정 파일이 포함된 커밋을 목록화 하고 싶어](#%ED%8A%B9%EC%A0%95-%ED%8C%8C%EC%9D%BC%EC%9D%B4-%ED%8F%AC%ED%95%A8%EB%90%9C-%EC%BB%A4%EB%B0%8B%EC%9D%84-%EB%AA%A9%EB%A1%9D%ED%99%94-%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [커밋을 참조하는 태그를 찾고 싶어](#%EC%BB%A4%EB%B0%8B%EC%9D%84-%EC%B0%B8%EC%A1%B0%ED%95%98%EB%8A%94-%ED%83%9C%EA%B7%B8%EB%A5%BC-%EC%B0%BE%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [서브모듈](#%EC%84%9C%EB%B8%8C%EB%AA%A8%EB%93%88)
- [모든 서브모듈을 클론하기](#%EB%AA%A8%EB%93%A0-%EC%84%9C%EB%B8%8C%EB%AA%A8%EB%93%88%EC%9D%84-%ED%81%B4%EB%A1%A0%ED%95%98%EA%B8%B0)
- [서브모듈 지우기](#%EC%84%9C%EB%B8%8C%EB%AA%A8%EB%93%88-%EC%A7%80%EC%9A%B0%EA%B8%B0)
- [기타 항목들](#%EA%B8%B0%ED%83%80-%ED%95%AD%EB%AA%A9%EB%93%A4)
- [지운 파일 복구하기](#%EC%A7%80%EC%9A%B4-%ED%8C%8C%EC%9D%BC-%EB%B3%B5%EA%B5%AC%ED%95%98%EA%B8%B0)
- [태그 지우기](#%ED%83%9C%EA%B7%B8-%EC%A7%80%EC%9A%B0%EA%B8%B0)
- [지워진 태그 복구하기](#%EC%A7%80%EC%9B%8C%EC%A7%84-%ED%83%9C%EA%B7%B8-%EB%B3%B5%EA%B5%AC%ED%95%98%EA%B8%B0)
- [지워진 패치](#%EC%A7%80%EC%9B%8C%EC%A7%84-%ED%8C%A8%EC%B9%98)
- [Zip파일로 레파지토리 추출하기](#zip%ED%8C%8C%EC%9D%BC%EB%A1%9C-%EB%A0%88%ED%8C%8C%EC%A7%80%ED%86%A0%EB%A6%AC-%EC%B6%94%EC%B6%9C%ED%95%98%EA%B8%B0)
- [파일 추적하기](#%ED%8C%8C%EC%9D%BC-%EC%B6%94%EC%A0%81%ED%95%98%EA%B8%B0)
- [파일 내용엔 변경이 없이 파일 이름을 앞글자만 대문자로 바꾸고 싶어](#%ED%8C%8C%EC%9D%BC-%EB%82%B4%EC%9A%A9%EC%97%94-%EB%B3%80%EA%B2%BD%EC%9D%B4-%EC%97%86%EC%9D%B4-%ED%8C%8C%EC%9D%BC-%EC%9D%B4%EB%A6%84%EC%9D%84-%EC%95%9E%EA%B8%80%EC%9E%90%EB%A7%8C-%EB%8C%80%EB%AC%B8%EC%9E%90%EB%A1%9C-%EB%B0%94%EA%BE%B8%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [깃 풀 받을때 로컬 파일을 덮어씌우고 싶어](#%EA%B9%83-%ED%92%80-%EB%B0%9B%EC%9D%84%EB%95%8C-%EB%A1%9C%EC%BB%AC-%ED%8C%8C%EC%9D%BC%EC%9D%84-%EB%8D%AE%EC%96%B4%EC%94%8C%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [파일을 로컬에는 보관하고 깃에서 지우고 싶어](#%ED%8C%8C%EC%9D%BC%EC%9D%84-%EB%A1%9C%EC%BB%AC%EC%97%90%EB%8A%94-%EB%B3%B4%EA%B4%80%ED%95%98%EA%B3%A0-%EA%B9%83%EC%97%90%EC%84%9C-%EC%A7%80%EC%9A%B0%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [특정한 버전대로 파일을 복구하고 싶어](#%ED%8A%B9%EC%A0%95%ED%95%9C-%EB%B2%84%EC%A0%84%EB%8C%80%EB%A1%9C-%ED%8C%8C%EC%9D%BC%EC%9D%84-%EB%B3%B5%EA%B5%AC%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [커밋과 브랜치 간의 특정 파일 변경 이력이 필요해](#%EC%BB%A4%EB%B0%8B%EA%B3%BC-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EA%B0%84%EC%9D%98-%ED%8A%B9%EC%A0%95-%ED%8C%8C%EC%9D%BC-%EB%B3%80%EA%B2%BD-%EC%9D%B4%EB%A0%A5%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%B4)
- [설정](#%EC%84%A4%EC%A0%95)
- [깃 명령어 몇 개를 앨리어스 등록하고 싶어](#%EA%B9%83-%EB%AA%85%EB%A0%B9%EC%96%B4-%EB%AA%87-%EA%B0%9C%EB%A5%BC-%EC%95%A8%EB%A6%AC%EC%96%B4%EC%8A%A4-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [레파지토리에 빈 디렉토리를 추가하고 싶어](#%EB%A0%88%ED%8C%8C%EC%A7%80%ED%86%A0%EB%A6%AC%EC%97%90-%EB%B9%88-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC%EB%A5%BC-%EC%B6%94%EA%B0%80%ED%95%98%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [레파지토리 유저명과 비밀번호를 캐시해두고 싶어](#%EB%A0%88%ED%8C%8C%EC%A7%80%ED%86%A0%EB%A6%AC-%EC%9C%A0%EC%A0%80%EB%AA%85%EA%B3%BC-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8%EB%A5%BC-%EC%BA%90%EC%8B%9C%ED%95%B4%EB%91%90%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [깃이 권한과 파일모드 변경을 무시하게 만들고 싶어](#%EA%B9%83%EC%9D%B4-%EA%B6%8C%ED%95%9C%EA%B3%BC-%ED%8C%8C%EC%9D%BC%EB%AA%A8%EB%93%9C-%EB%B3%80%EA%B2%BD%EC%9D%84-%EB%AC%B4%EC%8B%9C%ED%95%98%EA%B2%8C-%EB%A7%8C%EB%93%A4%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [글로벌 유저로 설정해두고 싶어](#%EA%B8%80%EB%A1%9C%EB%B2%8C-%EC%9C%A0%EC%A0%80%EB%A1%9C-%EC%84%A4%EC%A0%95%ED%95%B4%EB%91%90%EA%B3%A0-%EC%8B%B6%EC%96%B4)
- [뭘 잘못했는지 모르겠어](#%EB%AD%98-%EC%9E%98%EB%AA%BB%ED%96%88%EB%8A%94%EC%A7%80-%EB%AA%A8%EB%A5%B4%EA%B2%A0%EC%96%B4)
- [다른 리소스](#%EB%8B%A4%EB%A5%B8-%EB%A6%AC%EC%86%8C%EC%8A%A4)
- [도서](#%EB%8F%84%EC%84%9C)
- [튜토리얼](#%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC)
- [스크립트와 도구](#%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%99%80-%EB%8F%84%EA%B5%AC)
- [GUI 클라이언트](#gui-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8)
## 레파지토리
### 로컬 레파지토리에서 시작하고 싶어
이미 존재하는 프로젝트 디렉토리를 깃 레파지토리로 최적화해 쓰려면:
```sh
(my-folder) $ git init
```
### 난 리모트 레파지토리를 복제해오고 싶어
리모트 레파지토리를 클론하려면, 레파지토리 URL을 복사해와서 실행해요.
```sh
$ git clone [url]
```
폴더 이름이 리모트 레파지토리 이름과 같이 저장될 거에요.
복제할 리모트 서버의 연결을 확인하세요.(대부분 인터넷 연결을 확인하란 뜻이에요)
다른 레파지토리 이름으로 복제를 해오고 싶다면
```sh
$ git clone [url] name-of-new-folder
```
## 커밋 수정
### 내가 방금 어떤 커밋을 남겼지?
`git commit -a` 로 막 커밋을 남기고 내가 뭐라고 안에 적었더라? 한다고 하고. 최근의 커밋을 현재 HEAD에서 볼 수 있어요.
```sh
(main)$ git show
```
또는
```sh
$ git log -n1 -p
```
만약 특정 커밋의 파일을 보고 싶다면, 이렇게 할 수도 있어요. (commitID는 바로 당신이 관심있는 그 commit이에요)
```sh
$ git show :filename
```
### 커밋 메세지를 잘못 썼어
만약 메시지를 잘못 썼고 아직 푸시를 안했다면, 커밋 메시지 바꾸기를 따라해 볼 수 있어요.
```sh
$ git commit --amend --only
```
이 방법은 편집 가능한 기본 텍스트 에디터가 열릴텐데요, 다른 방법으론 한줄에 쓸 수도 있어요.
```sh
$ git commit --amend --only -m 'xxxxxxx'
```
만약 푸시를 이미 했다면, 커밋을 수정해서 강제 푸시를 할 수 있긴한대 별로 추천하진 않아요.
### 커밋을 다른 이름과 이메일 설정으로 해버렸어
하나의 커밋이라면 이렇게 수정해요.
```sh
$ git commit --amend --no-edit --author "New Authorname "
```
대안으로는 `git config --global author.(name|email)`에서 설정을 다시 맞춘 다음
```sh
$ git commit --amend --reset-author --no-edit
```
만약 전체 이력 변경이 필요하다면, `git filter-branch`의 설명 페이지를 봐요.
### 지난 커밋에서 파일 하나를 지우고 싶어
지난 커밋에서 파일 변경내역을 지우려면, 이렇게 해봐요:
```sh
$ git checkout HEAD^ myfile
$ git add myfile
$ git commit --amend --no-edit
```
그 파일이 새롭게 커밋으로 추가됐고 그 파일만 지우고 (git 에서만) 싶은 경우엔,
```sh
$ git rm --cached myfile
$ git commit --amend --no-edit
```
이 방법은 열린 패치가 있고 불필요한 파일을 커밋 했거나 리모트에 강제 푸시로 패치를 업데이트 해야할때 특히 유용해요. `--no-edit` 옵션은 기존 커밋 메세지를 유지하는데 사용돼요.
### 마지막 커밋을 지우고 싶어
푸시된 커밋을 지우고 싶다면 이걸 따라하면 되는데, 이력을 돌이킬 수 없게 되고 레파지토리에서 이미 풀을 받아간 다른 사람의 이력도 엉망이 되요. 간단히 말하자면, 잘 모르겠으면 절대 하지마요.
```sh
$ git reset HEAD^ --hard
$ git push --force-with-lease [remote] [branch]
```
아직 푸시 안했으면, 리셋으로 마지막 커밋 전 상태로 돌아가요. (변경점은 스테이지에 두고서)
```
(my-branch)$ git reset --soft HEAD^
```
이 방법은 푸시를 안 했을 때만 동작해요. 푸시를 했으면, 안전한 방법은 `git revert SHAofBadCommit` 한가지 밖이에요.
이 방법은 모든 지난 커밋 변경점으로 되돌아간 새 커밋을 만들 거에요. 또는, 만약 푸시한 브랜치가 리베이스에 안전하다면 (만약 다른 사람이 풀 받지 않는다면), `git push --force-with-lease` 명령어를 쓸수 있어요.
더 알고 싶다면, [이 섹션](#deleteremove-last-pushed-commit)을 참고해주세요.
### 임의적인 커밋 지우기
이전과 동일한 경고에요. 가능한 이 방법은 쓰지 마세요.
```sh
$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push --force-with-lease [remote] [branch]
```
아니면 [대화형 리베이스](#interactive-rebase)를 쓰고 지우고 싶은 커밋 라인을 지워도 돼요.
### 수정된 커밋을 푸시했는데, 에러 메세지가 떠
```sh
To https://github.com/yourusername/repo.git
! [rejected] mybranch -> mybranch (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```
알아두세요, 리베이스(아래를 보세요)나 어멘드는 **기존 커밋을 새걸로 바꿔요**,
그래서 이미 먼저 수정된 커밋이 푸시됐다면 강제 푸시를 해야 해요.
이 방법을 쓸땐 조심하세요; *항상* 작업되는 브랜치가 맞나 확인해요!
```sh
(my-branch)$ git push origin mybranch --force-with-lease
```
일반적으로 강제 푸시를 쓰지 마세요.
새 커밋을 만들어서 푸시하는게 수정된 커밋을 강제로 푸시하는 것보다 훨씬 나아요. 그런 수정된 커밋은 그 브랜치나 다른 자식 브랜치를 쓰는 다른 개발자의 소스 이력과 충돌의 원인이 될거에요.
`--force-with-lease` 는 여전히 실패할텐데, 누군가가 같은 브랜치를 쓴다면 변경점을 덮어쓰는 푸시를 할 수도 있어요.
절대로 아무도 같은 브랜치를 안 쓰거나, 절대로 브랜치에 업데이트를 해야할때 `--force` (`-f`) 옵션을 쓸 수 있지만 일반적으론 피하는게 좋아요.
### 하드 리셋을 해버렸는데 되돌리고 싶어
만약 하드 리셋을 했다고 해도 커밋을 돌릴 순 있어요. 깃은 며칠간은 로그를 가지고 있거든요.
알아두기 : 이건 커밋을 남겼거나 스테이시같이 백업을 했을 때만 유효해요. `git reset --hard` 은 커밋되지 않은 수정사항을 _다 지울 거에요_, 그러니 조심해서 써야해요. (안전한 방법으론 `git reset --keep` 이 있어요)
```sh
(main)$ git reflog
```
지난 커밋과 리셋을 위한 커밋을 볼 수 있을 거에요. 돌아가고 싶은 커밋의 SHA 코드를 골라서, 리셋을 해요:
```sh
(main)$ git reset --hard SHA1234
```
계속 할 수 있을거에요.
### 머지를 실수로 커밋, 푸시해버렸어
만약 실수로 머지할 준비가 안된 피쳐 브랜치를 메인 브랜치에 머지했어도 되돌릴 순 있어요.
하지만 문제는 있어요: 머지 커밋은 한개 이상의 부모(보통은 두 개)를 가지게 돼요.
사용하려면
```sh
(feature-branch)$ git revert -m 1
```
여기서 -m 1 옵션은 부모 번호 1(머지가 만들어진 브랜치)을 되돌릴 상위 항목으로 선택하라고 해요.
알아두기 : 부모 번호는 커밋 식별자가 아니고, 오히려 머지된 커밋이 `Merge: 8e2ce2d 86ac2e7` 이라는 라인을 가지고 있어요.
부모 번호는 이 라인에서 원하는 부모의 1 기반 인덱스이고, 첫번째 식별자는 1, 다음은 2 이렇게 이어져요.
## 스테이지
### 지난 커밋에 스테이지 변경점을 추가하고 싶어
```sh
(my-branch*)$ git commit --amend
```
### 전체가 아닌 새 파일만 스테이지에 올리고 싶어
보통은 부분적으로 파일을 스테이지하려면 이렇게 해요:
```sh
$ git add --patch filename.x
```
`-p`는 축약된 옵션이에요. 이 방식은 대화형 모드를 열텐데요. `s` 옵션을 쓰면 커밋을 나눌 수 있어요. 하지만 새 파일이라면 그런 옵션이 없을거에요. 새 파일을 추가하려면:
```sh
$ git add -N filename.x
```
그 다음 임의적으로 라인들을 골라 추가해주려면 `e`옵션이 필요할거에요. `git diff --cached`나 `git diff --staged`는 로컬에 저장된 부분과 스테이지에 있는 라인들을 비교해서 보여줄 거에요.
### 하나의 파일 변경점을 두개의 다른 커밋에 남기고 싶어
`git add`는 전체 파일들을 커밋에 추가해요. `git add -p`는 대화형으로 추가하고픈 변경점들을 고를 수 있어요.
### 아직 스테이지에 안 올라간 변경점을 스테이지에 추가하고, 스테이지에 있는 변경점을 다시 빼고 싶어
이건 좀 꼼수인데요, 스테이지 전인 파일들을 스테이시해서 빼두고선 리셋 할 수 있을거에요. 그 다음 스테이시를 다시 불러와 추가를 해요.
```sh
$ git stash -k
$ git reset --hard
$ git stash pop
$ git add -A
```
## 스테이지 전의 변경점
### 스테이지 전의 변경점을 새 브랜치로 옮기고 싶어
```sh
$ git checkout -b my-branch
```
### 스테이지전 변경점을 만들어둔 다른 브랜치로 옮기고 싶어
```sh
$ git stash
$ git checkout my-branch
$ git stash pop
```
### 내 로컬에 있는 커밋 안된 변경점을 다 무시하고 싶어 (스테이징 됐던 안됐던)
만약 모든 스테이징 됐거나 안 된 변경점을 버리고 싶다면 이렇게 해요:
```sh
(my-branch)$ git reset --hard
# or
(main)$ git checkout -f
```
이 방법은 `git add`로 스테이징된 모든 파일이 빠지게 돼요.
```sh
$ git reset
```
이 방법은 커밋되지 않은 모든 로컬 변경점이 되돌려 져요. (레파지토리 최상단 루트에서 실행해야 할거에요)
```sh
$ git checkout .
```
또 커밋되지 않은 변경점들 중 몇가지 파일이나 디렉토리만 되돌릴 수 있어요.
```sh
$ git checkout [some_dir|file.txt]
```
거기에 또 다른 되돌리는 방법으로 (타이핑 칠게 많지만 어떤 하위 디렉토리에서도 돼요):
```sh
$ git reset --hard HEAD
```
이 방법은 모든 트래킹되지 않은 파일들을 지워요, 그래서 깃에서 트래킹되는 파일들만 남아요:
```sh
$ git clean -fd
```
`-x` 옵 또한 무시된 파일들을 다 지워요.
### 스테이지 안된 특정 변경점을 지우고 싶어
작업중인 영역에서 전체가 아닌 특정 부분을 지우고 싶을때
원치않는 변경점을 확인하고, 변경점을 잘 보관하세요.
```sh
$ git checkout -p
# 날리고 싶은 사항에 y를 적으세요
```
또다른 전략은 `stash`을 같이 쓰는거에요. 챙겨야 하는 변경점을 스테이시 하고, 작업 중인 영역을 리셋하고, 다시 올바른 변경점으로 재적용해요.
```sh
$ git stash -p
# 저장하고 싶은 사항들을 다 선택하세요
$ git reset --hard
$ git stash pop
```
대안으로, 원치않는 변경점을 스테이시해서 그걸 날리는 방법도 있어요.
```sh
$ git stash -p
# 저장하고 싶지 앟은 사항들을 다 선택하세요
$ git stash drop
```
### 스테이지 안된 특정 파일을 지우고 싶어
작업 영역에서 특정 파일을 지우고 싶을 때.
```sh
$ git checkout myFile
```
대안으로, 작업영역 내 여러 파일들을 지우고 싶을때 모두 나열해서 적어요.
```sh
$ git checkout myFirstFile mySecondFile
```
### 로컬에 있는 스테이지 안된 변경점만 지우고 싶어
모든 스테이징 안된 커밋 전인 변경점을 지우고 싶을 때
```sh
$ git checkout .
```
### 트래킹 안된 파일들 다 지우고 싶어
트래킹 안된 파일들 다 지우고 싶을 땐
```sh
$ git clean -f
```
## 브랜치
### 모든 브랜치 리스트를 보고 싶어
로컬 브랜치 다 보기
```sh
$ git branch
```
리모트 브랜치 다 보기
```sh
$ git branch -r
```
로컬과 리모트 브랜치 모두 보기
```sh
$ git branch -a
```
### 커밋에서 브랜치 만들기
```sh
$ git checkout -b
```
### 다른 브랜치에서 풀을 받아와버렸어
이건 잘못된 풀을 받기전 HEAD가 어딜 가르키고 있었는지 볼 수 있는 `git reflog`를 써볼 수 있는 기회에요.
```sh
(main)$ git reflog
ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward
c5bc55a HEAD@{1}: checkout: checkout message goes here
```
간단히 원하는 커밋으로 브랜치를 되돌릴 수 있어요:
```sh
$ git reset --hard c5bc55a
```
끝!
### 로컬의 커밋을 지워서 서버에 있는 내 브랜치와 맞추고 싶어
서버에 변경점을 푸시 안했는지부터 확인해요.
`git status` 가 오리진보다 몇개의 커밋들이 앞서 있는지 보여줄거에요:
```sh
(my-branch)$ git status
# On branch my-branch
# Your branch is ahead of 'origin/my-branch' by 2 commits.
# (use "git push" to publish your local commits)
#
```
오리진(리모트과 같은 상태의)로 맞추는 리셋을 하는 방법 중 하나는:
```sh
(my-branch)$ git reset --hard origin/my-branch
```
### 새 브랜치 대신에 마스터에 커밋을 해버렸어
마스터에 있으면서 새 브랜치를 만들어요:
```sh
(main)$ git branch my-branch
```
마스터 브랜치를 기존 커밋으로 리셋해요:
```sh
(main)$ git reset --hard HEAD^
```
`HEAD^`는 `HEAD^1`의 축약인데요. `HEAD^`의 첫번째 부모를 의미하고, 비슷한 `HEAD^2`는 두번째 부모를 의미해요. (머지는 두 부모를 가질 수 있죠)
알아두세요 `HEAD^2`는 `HEAD~2`과 같은게 아니에요. (더 자세한 정보는 [이 링크](http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde)를 참고해요 )
대안으로, `HEAD^`를 쓰고 싶지 않다면, 마스터 브랜치로 옮길 커밋 해시를 알아둬요 (`git log`가 트릭을 부릴 거에요) 그리고 그 해쉬로 리셋을 해요. `git push`가 리모트랑 변경점이 똑같은걸 확인해줄거에요.
예를 들자면, 그 마스터의 커밋의 해쉬가 `a13b85e`라면:
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
새 브랜치로 체크아웃 해서 계속 작업을 해요:
```sh
(main)$ git checkout my-branch
```
### 다른 레퍼런스 같은 곳에서 모든 파일을 유지하고 싶어
수백번의 변경점을 가진 스파이크(아래 알아두기 참고) 작업을 한다고 가정해보죠. 모든 건 동작하고 있고,그 작업을 저장해두기 위해 다른 브랜치로 커밋을 해요:
```sh
(solution)$ git add -A && git commit -m "Adding all changes from this spike into one big commit."
```
그 커밋을 브랜치(아마 feature일수도 있고, `develop` 일수도 있겠죠)에 넣고 싶을 때, 모든 파일을 지키는데 관심이 있을거에요. 큰 커밋을 작게 나누고 싶을거에요.
현재 가지고 있는건:
* 스파이크를 위한 솔루션과 함께인 `solution` 브랜치. `develop` 브랜치의 1단계 앞선 상태.
* 변경점을 추가하고 싶은 `develop` 브랜치
브랜치로 내용들을 불러오는 것으로 해결할 수 있어요:
```sh
(develop)$ git checkout solution -- file1.txt
```
`develop`브랜치에서 `solution` 브랜치의 저 파일의 내용들을 얻을 수 있어요.
```sh
# On branch develop
# Your branch is up-to-date with 'origin/develop'.
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: file1.txt
```
그 다음, 평소처럼 커밋해요.
알아두기 : 스파이크 솔루션은 문제를 분석하거나 풀기위해 만들어졌어요. 이 솔루션들은 모두가 문제의 확실한 시각화를 얻고선 평가되고 제거돼요.~ [위키피디아](https://en.wikipedia.org/wiki/Extreme_programming_practices).
### 한 브랜치에 다른 브랜치에 남겼어야 하는 커밋을 여러개 남겼어
마스터 브랜치에 있다고 가정하고 `git log` 해보면 커밋 두개 볼 수 있을거에요:
```sh
(main)$ git log
commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee
Date: Tue Jul 22 15:39:27 2014 -0400
Bug #21 - Added CSRF protection
commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee
Date: Tue Jul 22 15:39:12 2014 -0400
Bug #14 - Fixed spacing on title
commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose
Date: Tue Jul 21 01:12:48 2014 -0400
First commit
```
각 버그커밋의 해쉬를 가져와요. (21번은 `e3851e8`, 14번은 `5ea5173`)
우선, 마스터 브랜치의 정확한 커밋 (`a13b85e`)으로 리셋해요:
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
그리고, 21번 버그 작업을 위한 새로운 브랜치를 만들수 있어요:
```sh
(main)$ git checkout -b 21
(21)$
```
그리고 21번 버그 커밋을 *체리픽* 해서 브랜치 최상단에 올려요. 그 커밋을 적용할건데, 오직 그 커밋만을 헤드에 뭐가 있든 최상단으로 적용할거란 의미에요.
```sh
(21)$ git cherry-pick e3851e8
```
이 지점에서 충돌이 있을 수도 있어요.
어떻게 충돌을 해결할지 [대화형 리베이스 섹션](#interactive-rebase) 안에 있는 [**충돌이 있어**](#merge-conflict) 부분을 참고하세요.
자 이제 14번 버그 작업을 위해 마스터로 가서 새 브랜치를 만들어요.
```sh
(21)$ git checkout main
(main)$ git checkout -b 14
(14)$
```
그리고 마지막으로, 14번 버그작업을 위한 커밋을 체리픽해요.
```sh
(14)$ git cherry-pick 5ea5173
```
### 업스트림에선 지워진 로컬 브랜치를 지우고 싶어
깃헙에 풀리퀘스트로 머지를 하면, 포크 뜬 머지 브랜치를 지울껀지 선택할 수 있는 옵션을 줘요.
해당 브랜치에 계속 작업할 예정이 없다면, 다량의 오래된 브랜치들로 뒤덮이지 않게 로컬 작업을 지워주는게 더 깔끔해요.
```sh
$ git fetch -p upstream
```
여기서, `upstream`은 패치로 가져오려는 리모트 레파지토리에요.
### 브랜치를 지워버렸어
주기적으로 리모트으로 푸시한다면, 대부분은 안전해야 해요. 그치만 가끔은 브랜치를 지울 수 있어요. 새 브랜치를 만들고 파일을 하나 만들었다고 해보죠:
```sh
(main)$ git checkout -b my-branch
(my-branch)$ git branch
(my-branch)$ touch foo.txt
(my-branch)$ ls
README.md foo.txt
```
추가하고 커밋해요.
```sh
(my-branch)$ git add .
(my-branch)$ git commit -m 'foo.txt added'
(my-branch)$ foo.txt added
1 files changed, 1 insertions(+)
create mode 100644 foo.txt
(my-branch)$ git log
commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
Author: siemiatj
Date: Wed Jul 30 00:34:10 2014 +0200
foo.txt added
commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson
Date: Tue Jul 29 13:14:46 2014 -0400
Fixes #6: Force pushing after amending commits
```
이제 다시 마스터로 돌아가 '실수로' 브랜치를 지워보죠.
```sh
(my-branch)$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
(main)$ git branch -D my-branch
Deleted branch my-branch (was 4e3cd85).
(main)$ echo oh noes, deleted my branch!
oh noes, deleted my branch!
```
여기에서 업그레이드된 로그 도구인 '리플로그'에 익숙해져야 해요. 리플로그는 레파지토리의 모든 행동의 이력을 다 보관해요.
```
(main)$ git reflog
69204cd HEAD@{0}: checkout: moving from my-branch to main
4e3cd85 HEAD@{1}: commit: foo.txt added
69204cd HEAD@{2}: checkout: moving from main to my-branch
```
보시다시피 지워진 브랜치의 커밋 해쉬도 볼 수 있어요. 지웠던 브랜치를 살릴 수 있는 지 한번 해보죠.
```sh
(main)$ git checkout -b my-branch-help
Switched to a new branch 'my-branch-help'
(my-branch-help)$ git reset --hard 4e3cd85
HEAD is now at 4e3cd85 foo.txt added
(my-branch-help)$ ls
README.md foo.txt
```
짜잔! 지워진 파일들을 되돌려 놨어요. `git reflog`는 리베이스가 끔찍하게 잘못 됐을때 아주 유용해요.
### 브랜치를 지우고 싶어
리모트 브랜치를 삭제하려면:
```sh
(main)$ git push origin --delete my-branch
```
이렇게도:
```sh
(main)$ git push origin :my-branch
```
로컬 브랜치를 삭제하려면:
```sh
(main)$ git branch -d my-branch
```
현재 브랜치나 업스트림에 머지되지 않은 로컬 브랜치를 지우려면:
```sh
(main)$ git branch -D my-branch
```
### 여러개의 브랜치를 지우고 싶어
`fix/`로 시작하는 모든 브랜치들을 지우고 싶다면:
```sh
(main)$ git branch | grep 'fix/' | xargs git branch -d
```
### 브랜치 이름을 바꾸고 싶어
현재 (로컬) 브랜치 이름을 바꾸려면:
```sh
(main)$ git branch -m new-name
```
다른 (로컬) 브랜치 이름을 바꾸려면
```sh
(main)$ git branch -m old-name new-name
```
### 다른 사람이 작업중인 리모트 브랜치로 체크아웃 하고 싶어
우선, 리모트 레파지토리에서 모든 브랜치를 패치 받아요:
```sh
(main)$ git fetch --all
```
리모트의 `daves`로 체크아웃 하고 싶다고 하면.
```sh
(main)$ git checkout --track origin/daves
Branch daves set up to track remote branch daves from origin.
Switched to a new branch 'daves'
```
(`--track` 은 `git checkout -b [branch] [remotename]/[branch]` 의 축약이에요)
`daves` 브랜치의 로컬 카피를 줄거에요. 그리고 푸시된 업데이트들도 리모트로 표시돼요.
### 현재 로컬 브랜치로 새로운 리모트 브랜치를 만들고 싶어
```sh
$ git push HEAD
```
또한 리모트 브랜치를 현재 브랜치를 위한 업스트림으로 설정하고 싶다면, 대신 아래 방법을 써봐요:
```sh
$ git push -u HEAD
```
`push.default` 설정의 `upstream` 모드와 `simple`모드 (2.0 버전의 깃의 기본)와 함께,
아래 커맨드는 이전에 `-u` 옵션으로 등록된 리모트 브랜치와 관련된 현재 브랜치를 푸시할거에요:
```sh
$ git push
```
`git push`의 다른 모드의 동작은 [`push.default` 문서](https://git-scm.com/docs/git-config#Documentation/git-config.txt-pushdefault)에 설명돼 있어요.
### 리모트 브랜치를 로컬 브랜치를 위한 업스트림으로 설정하고 싶어
리모트 브랜치를 현재 쓰고 있는 로컬 브랜치를 위한 업스트림으로 설정할 수 있어요:
```sh
$ git branch --set-upstream-to [remotename]/[branch]
# 아니면 짧게:
$ git branch -u [remotename]/[branch]
```
다른 로컬 브랜치를 위한 업스트림 리모트 브랜치를 설정하려면:
```sh
$ git branch -u [remotename]/[branch] [local-branch]
```
### HEAD를 기본 리모트 브랜치로 트래킹하도록 설정하고 싶어
리모트 브랜치를 확인해보는 것으로, HEAD가 트래킹 중인 리모트 브랜치를 볼 수 있어요. 몇몇 경우에는, 원하던 브랜치가 아닐거에요.
```sh
$ git branch -r
origin/HEAD -> origin/gh-pages
origin/main
```
`origin/HEAD`를 `origin/main`를 트래킹하는 것으로 변경하려면, 이 커맨드로 실행할 수 있어요:
```sh
$ git remote set-head origin --auto
origin/HEAD set to main
```
### 다른 브랜치에 변경점을 잘못 남겼어
커밋 되지 않은 변경점, 거기다 잘못된 브랜치에 하고 있었다면 변경점을 스테이시 하고 원하는 브랜치로 가 스테이시 어플라이 해요:
```sh
(wrong_branch)$ git stash
(wrong_branch)$ git checkout
(correct_branch)$ git stash apply
```
## 리베이스와 머지
### 리베이스/머지 한 걸 되돌리고 싶어
현재 브랜치를 의도하지 않던 브랜치로 머지 또는 리베이스 했거나, 리베이스/머지 도중에 완료하거나 끝내지 못했을거에요.
깃은 위험한 과정 전에 원래의 HEAD 포인트를 ORIG_HEAD라 불리는 변수에 보관해요, 그러니 리베이스/머지 전 상태로 브랜치를 복구하기 간단해요.
```sh
(my-branch)$ git reset --hard ORIG_HEAD
```
### 리베이스를 했는데, 강제 푸시하고 싶진 않아
아쉽게도 그런 변경점을 리모트 브랜치에 반영하려면 강제 푸시밖에 방법이 없어요. 이력을 변경해왔기 때문이죠.
리모트 브랜치는 강제 푸시 외엔 적용 해주지 않을거에요.
많은 분들이 머지 워크플로우를 리베이스 워크플로우보다 선호하는 많이 이유 중 하나죠 - 큰 팀에선 개발자의 강제 푸시로 곤란해질 수 있어요.
주의해서 쓰세요.
리베이스를 그나마 안전하게 쓰는 방법은 리모트 브랜치의 모든 변경점과 똑같이 반영하는게 아니라 대신에 이렇게 해봐요:
```sh
(main)$ git checkout my-branch
(my-branch)$ git rebase -i main
(my-branch)$ git checkout main
(main)$ git merge --ff-only my-branch
```
더 확인이 필요하다면, [이 스택오버플로우의 쓰레드](https://stackoverflow.com/questions/11058312/how-can-i-use-git-rebase-without-requiring-a-forced-push)를 참고해요.
### 커밋끼리 합치고 싶어
`main`에 풀 리퀘스트가 될 브랜치에서 작업하고 있다고 가정해봐요.
가장 간단한 경우는 원하는게 *모든* 커밋을 하나의 커밋으로 합치고 변경점의 시간을 신경쓰지 않아도 되는 것일 때, 리셋하고 커밋 다시하면 돼요.
마스터 브랜치가 최신이고 모든 변경점이 커밋된 것만 확인한 다음:
```sh
(my-branch)$ git reset --soft main
(my-branch)$ git commit -am "New awesome feature"
```
좀더 조정하고, 시간기록까지 보관하고 싶다면, 대화형 리베이스가 필요할거에요.
```sh
(my-branch)$ git rebase -i main
```
만약 다른 브랜치로 붙는 작업을 하는게 아니라면, `HEAD`을 기준으로 리베이스 해야해요.
예로 마지막 2개의 커밋을 스쿼시(기존 커밋에 반영해넣는)하고 싶다면 `HEAD~2`로 리베이스 해요. 3개라면 `HEAD~3`으로 하구요.
```sh
(main)$ git rebase -i HEAD~2
```
대화형 리베이스를 실행하면 텍스트 에디터로 이런 것들을 볼 수 있을거에요.
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
pick b729ad5 fixup
pick e3851e8 another fix
# Rebase 8074d12..b729ad5 onto 8074d12
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```
모든 `#`으로 시작하는 주석줄은 리베이스에 영향을 주진 않아요.
다음으로 `pick` 부분을 다른 명령어로 바꾸거나, 해당하는 라인을 지워서 커밋을 지울 수도 있어요.
예를 들자면 **오래된 (첫번째) 커밋만 두고 두번째 오래된 커밋과 나머지를 다 합치고 싶을때**, 첫번째와 두번째 커밋 제외한 나머지 커맨드들을 `f`로 바꿔야 할거에요:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
f b729ad5 fixup
f e3851e8 another fix
```
이 커밋들을 합치고 **커밋 이름을 바꾸고 싶다면**, 추가로 적어줘야 해요 두번째 커밋 다음에 `r`를 추가하거나 간단히 `f` 대신 `s`를 추가해주면 될거에요:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
s b729ad5 fixup
s e3851e8 another fix
```
그런 다음에 한번 더 뜨는 텍스트 에디터로 커밋 이름을 바꿀 수 있어요.
```vim
Newer, awesomer features
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 8074d12
# You are currently editing a commit while rebasing branch 'main' on '8074d12'.
#
# Changes to be committed:
# modified: README.md
#
```
전부 다 성공하면, 이런 메세지를 볼거에요:
```sh
(main)$ Successfully rebased and updated refs/heads/main.
```
#### 안전한 머지 전략
`--no-commit`는 머지는 하지만 실패하고 자동 커밋이 안된것처럼 보이는데, 커밋하기전에 머지 결과를 보고 추가로 조정할 수 있게 해줘요.
`no-ff`는 피쳐 브랜치가 있었다는 증거를 남기고, 이력을 일관되게 가지게 해요.
```sh
(main)$ git merge --no-ff --no-commit my-branch
```
#### 브랜치를 커밋 하나로 머지해야해
```sh
(main)$ git merge --squash my-branch
```
#### 푸시 되지 않은 커밋만 합치고 싶어
가끔 여러가지 작업 도중인 커밋을 푸시하기 전에 합치고 싶을거에요.
다른 누군가가 벌써 참고해서 커밋을 만들고 있을테니 이미 푸시된 커밋을 잘못 합치길 바라진 않을거에요.
```sh
(main)$ git rebase -i @{u}
```
이 명령은 아직 푸시하지 않은 커밋만으로 대화형 리베이스를 실행해요. 그러니 목록 내에 있는 어떤 커밋이든 재정렬/수정/합치기 안전해요.
#### 머지를 중단해야해
때때로 머지는 어떤 파일에 문제를 일으킬 수도 있어요, 이 경우 옵션 `abort`으로 현재 충돌 해결 프로세스를 중단하고 병합하기 전 상태로 다시 구성할 수 있어요.
```sh
(my-branch)$ git merge --abort
```
이 명령은 1.7.4 버전부터 쓸 수 있어요.
### 브랜치내 모든 커밋이 머지됐는지 확인해
브랜치 내 모든 커밋이 다른 브랜치로 머지됐는지 확인하려면, 그 브랜치들 HEAD (또는 특정 커밋)를 비교해야해요:
```sh
(main)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
```
이 명령은 어디에는 있고 다른덴 없는 커밋이 있나를 알려줄거에요 그리고 브랜치들 사이에 공유되지 않은게 목록을 보여줄 거구요. 다른 옵션은 이렇게:
```sh
(main)$ git log main ^feature/120-on-scroll --no-merges
```
### 대화형 리베이스로 발생가능한 이슈
#### 리베이스 편집 화면에서 'noop'
이런 걸 본다면:
```
noop
```
동일한 커밋에 있거나 현재 브랜치보다 앞서 있는 브랜치에 대해 리베이스를 시도한다는 의미에요. 이렇게 해볼 수 있어요:
* 마스터 브랜치가 있어야 할 곳에 있나 확인
* 대신해서 `HEAD~2` 또는 더 기존 항목을 리베이스
#### 충돌이 있어
리베이스를 똑바로 끝내지 못했다면, 충돌을 해결해야 할거에요.
어떤 파일이 충돌났는지 `git status`를 먼저 실행해봐요:
```sh
(my-branch)$ git status
On branch my-branch
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
both modified: README.md
```
이 예시에선, `README.md` 가 충돌났네요. 파일을 열어서 아래와 같은 부분을 찾아봐요:
```vim
<<<<<<< HEAD
some code
=========
some code
>>>>>>> new-commit
```
새로운 커밋으로 추가된 코드(예시에선, 중간 선부터 `new-commit` 까지의)와 `HEAD` 사이에서 차이점을 해결해야 할거에요.
어느 한쪽 브랜치의 코드를 남기고 싶다면, `--ours` 또는 `--theirs`를 쓰면 돼요:
```sh
(main*)$ git checkout --ours README.md
```
- *머지*할때, `--ours`를 쓰면 로컬 브랜치의 변경점 유지하고, `--theirs` 는 다른 브랜치의 변경점를 유지해요.
- *리베이스*할 땐, `--theirs`가 로컬 브랜치의 변경점을 유지하고 `--ours`는 다른 브랜치의 변경점을 유지해요. 이런 차이에 관한 설명은 Git 정식 문서 중 [이 문서](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---merge)를 보세요.
만약 머지가 더 복잡하면, 비주얼 디프 에디터를 쓸 수도 있어요:
```sh
(main*)$ git mergetool -t opendiff
```
코드의 충돌을 해결하고 테스트가 해결되고 나면, 바뀐 파일 내용을 `git add` 해주고, `git rebase --continue`로 리베이스를 이어서 해요.
```sh
(my-branch)$ git add README.md
(my-branch)$ git rebase --continue
```
만약 모든 충돌을 개선한 내용이 커밋 전과 동일한 트리 구조를 가진다면, 대신에 `git rebase --skip`를 해야 해요.
리베이스 중 멈추고 싶은 어떤 시점이거나 원래 상태의 브랜치로 돌아가고 싶다면, 이렇게 할 수 있어요:
```sh
(my-branch)$ git rebase --abort
```
## 스테이시
### 모든 변경점 스테이시 하기
작업중인 디렉토리에서의 변경한 내용 전부를 스테이시 하려면
```sh
$ git stash
```
트래킹 되지 않은 파일까지도 포함하려면, `-u` 옵션을 써요.
```sh
$ git stash -u
```
### 특정 파일들만 스테이시 하기
작업중인 디렉토리에서 한 파일만 스테이시 하기
```sh
$ git stash push working-directory-path/filename.ext
```
작업중인 디렉토리에서 여러 파일 스테이시 하기
```sh
$ git stash push working-directory-path/filename1.ext working-directory-path/filename2.ext
```
### 메세지와 함께 스테이시 하기
```sh
$ git stash save
```
### 특정 스테이시 목록에서 가져와 적용하기
메세지 작성된 스테이시 리스트 먼저 확인하세요
```sh
$ git stash list
```
그런 다음 리스트 내 특정 스테이시를 적용해요
```sh
$ git stash apply "stash@{n}"
```
여기에서, 'n' 은 스택 안에서 스테이시의 위치를 나타내요. 젤 위에 있는 스테이시가 0 일거에요.
## 찾아보기
### 어떤 커밋에서 문자열을 찾고 싶어
특정한 문자열이 포함된 어떤 커밋을 찾으려면, 이런 구조로 쓸 수 있어요:
```sh
$ git log -S "string to find"
```
일반적인 파라미터들은:
* `--source` 각 커밋에 도달한 명령어에 지정된 참조 이름을 보여주는걸 의미해요.
* `--all` 는 모든 브랜치에서 시작하는걸 의미해요.
* `--reverse` 반대의 순서로 출력해요, 변경점의 첫번째 커밋이 보일꺼란 거죠.
### 작성자나 커미터를 찾고 싶어
작성자나 커미터의 모든 커밋을 찾으려면 이렇게 쓸 수 있어요:
```sh
$ git log --author=
$ git log --committer=
```
작성자와 커미터가 같지 않다는 것만 염두해두세요.
`--author`는 코드를 실제로 코드를 작성한 사람이고
반면에 `--committer`는 실제 작성자를 대신해 커밋을 한 사람이에요.
### 특정 파일이 포함된 커밋을 목록화 하고 싶어
특정 파일이 든 모든 커밋을 찾으려면 이렇게 해요:
```sh
$ git log --
```
보통은 정확한 경로를 쓸테지만 와일드 카드로 경로나 파일명을 쓸수도 있어요:
```sh
$ git log -- **/*.js
```
와일드 카드를 쓸 때, 커밋된 파일의 목록을 볼 수 있는 `--name-status`로 확인하는게 유용할거에요:
```sh
$ git log --name-status -- **/*.js
```
### 커밋을 참조하는 태그를 찾고 싶어
특정 커밋이 포함된 모든 태그를 찾으려면:
```sh
$ git tag --contains
```
## 서브모듈
### 모든 서브모듈을 클론하기
```sh
$ git clone --recursive git://github.com/foo/bar.git
```
벌써 클론했다면:
```sh
$ git submodule update --init --recursive
```
### 서브모듈 지우기
서브모듈을 만드는건 아주 간단하지만 지우는건 그렇진 않아요. 필요한 명령어는:
```sh
$ git submodule deinit submodulename
$ git rm submodulename
$ git rm --cached submodulename
$ rm -rf .git/modules/submodulename
```
## 기타 항목들
### 지운 파일 복구하기
우선 그 파일이 마지막으로 있었던 커밋을 찾고:
```sh
$ git rev-list -n 1 HEAD -- filename
```
그런 다음 그 파일을 체크아웃해요
```
git checkout deletingcommitid^ -- filename
```
### 태그 지우기
```sh
$ git tag -d
$ git push :refs/tags/
```
### 지워진 태그 복구하기
이미 지운 태그를 복구하고 싶다면, 이런 단계를 따라해 볼 수 있어요: 우선, 연결할 수 없는 태그를 찾고:
```sh
$ git fsck --unreachable | grep tag
```
태그의 해쉬를 메모해두세요.
그런 다음 [`git update-ref`](https://git-scm.com/docs/git-update-ref)을 써서 지워진 태그를 복구해요:
```sh
$ git update-ref refs/tags/
```
이제 태그가 복구돼있을거에요.
### 지워진 패치
만약 깃헙에서 누군가가 풀리퀘스트를 보냈는데 이미 원래의 포크가 지워졌다면,
url을 쓸 수 없게 돼 레파지토리를 클론할 수 없거나 [.diff, .patch](https://github.com/blog/967-github-secrets)와 같은 `git am`를 쓸 수 없을 거에요.
하지만 [깃헙의 특별한 참조](https://gist.github.com/piscisaureus/3342247)을 이용해서 풀 리퀘스트 자체를 확인할 수 있어요.
PR#1의 내용을 pr_1이란 새 브랜치로 패치 받으려면:
```sh
$ git fetch origin refs/pull/1/head:pr_1
From github.com:foo/bar
* [new ref] refs/pull/1/head -> pr_1
```
### Zip파일로 레파지토리 추출하기
```sh
$ git archive --format zip --output /full/path/to/zipfile.zip main
```
## 파일 추적하기
### 파일 내용엔 변경이 없이 파일 이름을 앞글자만 대문자로 바꾸고 싶어
```sh
(main)$ git mv --force myfile MyFile
```
### 깃 풀 받을때 로컬 파일을 덮어씌우고 싶어
```sh
(main)$ git fetch --all
(main)$ git reset --hard origin/main
```
### 파일을 로컬에는 보관하고 깃에서 지우고 싶어
```sh
(main)$ git rm --cached log.txt
```
### 특정한 버전대로 파일을 복구하고 싶어
복구하고 싶은 해시가 c5f567 이라고 가정하면:
```sh
(main)$ git checkout c5f567 -- file1/to/restore file2/to/restore
```
c5f567 한 단계전으로 복구하고 싶다면, c5f567~1로 적어줘요:
```sh
(main)$ git checkout c5f567~1 -- file1/to/restore file2/to/restore
```
### 커밋과 브랜치 간의 특정 파일 변경 이력이 필요해
마지막 커밋과 c5f567으로 부터의 차이를 비교하고 싶다고 가정하면:
```sh
$ git diff HEAD:path_to_file/file c5f567:path_to_file/file
# 아니면 짧게:
$ git diff HEAD c5f567 -- path_to_file/file
```
브랜치도 같은 방법으로:
```sh
$ git diff main:path_to_file/file staging:path_to_file/file
# 아니면 짧게:
$ git diff main staging -- path_to_file/file
```
## 설정
### 깃 명령어 몇 개를 앨리어스 등록하고 싶어
맥OS나 리눅스에는, 깃 설정 파일이 ```~/.gitconfig``` 에 있어요. 단축용으로 (몇개는 평소 쓰는 용도로) 앨리어스 몇개를 아래와 같이 계속 추가해오고 있어요.
```vim
[alias]
a = add
amend = commit --amend
c = commit
ca = commit --amend
ci = commit -a
co = checkout
d = diff
dc = diff --changed
ds = diff --staged
f = fetch
loll = log --graph --decorate --pretty=oneline --abbrev-commit
m = merge
one = log --pretty=oneline
outstanding = rebase -i @{u}
s = status
unpushed = log @{u}
wc = whatchanged
wip = rebase -i @{u}
zap = fetch -p
```
### 레파지토리에 빈 디렉토리를 추가하고 싶어
못해요! 깃은 지원하지 않거든요, 근데 꼼수가 있어요. 디렉토리에에 .gitignore 파일을 아래 내용으로 만들어요:
```
# Ignore everything in this directory
*
# Except this file
!.gitignore
```
다른 일반적인 컨벤션은 그 폴더 안에 .gitkeep이라는 이름의 빈 파일을 만드는 거에요.
```sh
$ mkdir mydir
$ touch mydir/.gitkeep
```
.keep이란 이름으로도 쓸 수 있는데요, 두번째 라인이 ```touch mydir/.keep```가 되어야겠죠.
### 레파지토리 유저명과 비밀번호를 캐시해두고 싶어
인증이 필요한 레파지토리를 가지고 있을 텐데요.
이런 경우 유저명과 비밀번호를 캐시할 수 있을테니 매번 푸시/풀 할 때마다 입력할 필욘 없어요.
크리덴셜 헬퍼가 해줄거에요.
```sh
$ git config --global credential.helper cache
# Set git to use the credential memory cache
```
```sh
$ git config --global credential.helper 'cache --timeout=3600'
# Set the cache to timeout after 1 hour (setting is in seconds)
```
### 깃이 권한과 파일모드 변경을 무시하게 만들고 싶어
```sh
$ git config core.fileMode false
```
이 것을 로그인된 유저의 기본 행위로 설정으로 해두려면, 이렇게 써요:
```sh
$ git config --global core.fileMode false
```
### 글로벌 유저로 설정해두고 싶어
모든 로컬 레파지토리에 사용되는 유저 정보를 설정하려면, 그리고 버전 이력을 리뷰할때 알아보기 쉬운 이름으로 설정하려면:
```sh
$ git config --global user.name “[firstname lastname]”
```
각 이력 생산자에게 연관해서 이메일 설정을 해주려면:
```sh
git config --global user.email “[valid-email]”
```
## 뭘 잘못했는지 모르겠어
음, 망했군요. 뭔가를 `reset` 했거나, 다른 브랜치로 머지했거나, 지금은 찾질 못하는 커밋으로 강제 푸시를 해버렸군요.
알다시피, 어떤 시점에선, 잘 하고 있었고 거기로 돌아가고 싶겠죠.
이게 바로 `git reflog`의 존재이유에요. `reflog` 는 브랜치 끝의 어떤 변경점이든 브랜치나 태그에 의해 참조되지 않더라도 다 보관해요.
기본적으로, HEAD가 변경되는 모든 경우, 리플로그에 새로운 입력이 추가돼요. 아쉽게도 이 기능은 로컬 레파지토리에서만 동작해고, 오직 움직임만을 트래킹해요 (예를 들자면 어디에도 기록되지 않았던 파일의 변경은 아니에요)
```sh
(main)$ git reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
```
이 리플로그는 마스터에서 2.2 브랜치로 체크아웃하고 되돌린 것을 보여주네요.
저기에선, 오래된 커밋으로 리셋하기 어려워요. 최신 활동이 `HEAD@{0}` 상단 라벨로 보여지네요.
만약 실수로 뒤로 이동했다면,
리플로그는 실수로 지워진 2개의 커밋 전 상태인 (0254ea7)를 가리키는 커밋 마스터를 포함할거에요.
```sh
$ git reset --hard 0254ea7
```
`git reset`을 쓰는 것으로 마스터를 이전 커밋으로 되돌릴 수 있어요.
이력이 실수로 변경됐을 때의 안정망을 제공할거에요.
([여기](https://www.atlassian.com/git/tutorials/rewriting-history/git-reflog)에서 복제해와 수정했어요).
# 다른 리소스
## 도서
* [Pro Git](https://git-scm.com/book/en/v2) - Scott Chacon 과 Ben Straub의 훌륭한 깃 책
* [Git Internals](https://github.com/pluralsight/git-internals-pdf) - Scott Chacon의 또다른 훌륭한 깃 책
## 튜토리얼
* [Atlassian's Git tutorial](https://www.atlassian.com/git/tutorials) 기초부터 고급까지 튜토리얼과 함께 하는 깃 제대로 얻기
* [Learn Git branching](https://learngitbranching.js.org/) 대화형 웹 기반의 브랜치/머지/리베이스 튜토리얼
* [Getting solid at Git rebase vs. merge](https://medium.com/@porteneuve/getting-solid-at-git-rebase-vs-merge-4fa1a48c53aa)
* [git-workflow](https://github.com/asmeurer/git-workflow) - [Aaron Meurer](https://github.com/asmeurer)의 어떻게 깃을 통해 오픈소스 레파지토리에 어떻게 기여할까
* [GitHub as a workflow](https://hugogiraudel.com/2015/08/13/github-as-a-workflow/) - 깃헙을 워크플로우로 쓰는 흥미로운 작업, 특히 빈 풀 리퀘스트를 활용하는
* [Githug](https://github.com/Gazler/githug) - 더 일반적인 깃 워크플로우를 배우는 게임
## 스크립트와 도구
* [firstaidgit.io](http://firstaidgit.io/) 가장 많이 묻는 깃 질문의, 검색가능한 모음
* [git-extra-commands](https://github.com/unixorn/git-extra-commands) - 유용한 기타 깃 스크립트 모음
* [git-extras](https://github.com/tj/git-extras) - 깃 유틸리티 -- 레파지토리 요약, repl, 변경이력 밀집도, 작성자 커밋 비율 등
* [git-fire](https://github.com/qw3rtman/git-fire) - git-fire는 모든 현재 파일을 추가,커밋,새 브랜치로 푸시(머지를 예방하기 위한) 등 비상사태를 도와주는 플러그인
* [git-tips](https://github.com/git-tips/tips) - 자그마한 깃 팁들
* [git-town](https://github.com/Originate/git-town) - 포괄적이고, 높은 수준의 깃 워크플로우 지원! http://www.git-town.com
## GUI 클라이언트
* [GitKraken](https://www.gitkraken.com/) - 완전 고급의 깃 클라이언트 Windows, Mac & Linux
* [git-cola](https://git-cola.github.io/) - 또 다른 깃 클라이언트 Windows, OS X
* [GitUp](https://github.com/git-up/GitUp) - 아주 독단적으로 방식으로 깃의 복잡함을 다루는 새로운 GUI
* [gitx-dev](https://rowanj.github.io/gitx/) - 또다른 그래픽적인 깃 클라이언트 OS X
* [Sourcetree](https://www.sourcetreeapp.com/) - 아름답고 무료인 깃 GUI 안에서 단순함과 강력함이 만났어 Windows and Mac
* [Tower](https://www.git-tower.com/) - 그래픽 Git 클라이언트 OS X (유료)
* [tig](https://jonas.github.io/tig/) - 깃을 위한 터민러 텍스트 모드 인터페이스
* [Magit](https://magit.vc/) - Emacs 패키지를 위해 구현된 깃 인터페이스
* [GitExtensions](https://github.com/gitextensions/gitextensions) - 쉘 확장, 비주얼 스투디오 2010-2015 플러그인 그리고 독자적인 깃 레파지토리 도구
* [Fork](https://git-fork.com/) - 빠르고 친숙한 깃 클라이언트 Mac (베타)
* [gmaster](https://gmaster.io/) - 3-way 머지, 리팩터 분석기, 시멘틱 diff와 머지 기능의 윈도 전용 깃 클라이언트 (베타)
* [gitk](https://git-scm.com/docs/gitk) - 간단한 레파지토리 상태를 볼 수 있는 리눅스 깃 클라이언트
================================================
FILE: README_ru.md
================================================
# Правила полета на Git
🌍
*[English](README.md) ∙ [Español](README_es.md) ∙ [Русский](README_ru.md) ∙ [繁體中文](README_zh-TW.md) ∙ [简体中文](README_zh-CN.md) ∙ [한국어](README_kr.md) ∙ [Tiếng Việt](README_vi.md) ∙ [Français](README_fr.md) ∙ [日本語](README_ja.md)*
#### Что это за "правила полета" такие?
Есть инструкции для астронавтов (а здесь подобное руководство для пользователей Git) о том, как быть, если что-то пошло не так.
> *Полетные правила* - это с большим трудом полученный запас знаний, записанный в форме инструкций, в которых указан последовательный порядок действий в разных ситуациях и сказано, почему нужно делать именно так. По сути это крайне подробный, стандартный план действий в ситуациях, развивающихся по прописанным сценариям. [...]
> В НАСА фиксируют все наши промахи, аварии и методы решения проблем с начала 1960 х гг., когда наземные группы проекта Mercury начали составлять сборник "выученных уроков". Теперь в этом сборнике перечислены тысячи сложных ситуаций и их решения – от отказа двигателей, поломки ручек люка до проблем с компьютером.
— Крис Хэдфилд, *Руководство астронавта по жизни на Земле* (перевод Дмитрия Лазарева).
#### Соглашения для этого документа
Для наглядности во всех примерах в этом документе используется измененное приглашение командной строки, чтобы показать текущую ветку и есть ли подготовленные изменения. Ветка заключена в кавычки, а символ `*` после ветки означает подготовленные изменения.
Приведенные команды работают на Git версии 2.13.0 и выше. Для обновления Вашей версии Git посетите [вебсайт Git](https://www.git-scm.com/).
[](https://gitter.im/k88hudson/git-flight-rules?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
- [Репозитории](#%D0%A0%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B8)
- [Я хочу создать локальный репозиторий](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9)
- [Я хочу клонировать удаленный репозиторий](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BA%D0%BB%D0%BE%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9)
- [Я неправильно задал удаленный репозиторий](#%D0%AF-%D0%BD%D0%B5%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D1%8C%D0%BD%D0%BE-%D0%B7%D0%B0%D0%B4%D0%B0%D0%BB-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9)
- [Я хочу добавить код в чужой репозиторий](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D0%BA%D0%BE%D0%B4-%D0%B2-%D1%87%D1%83%D0%B6%D0%BE%D0%B9-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9)
- [Предложение кода с помощью пулл-реквестов](#%D0%9F%D1%80%D0%B5%D0%B4%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BA%D0%BE%D0%B4%D0%B0-%D1%81-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E-%D0%BF%D1%83%D0%BB%D0%BB-%D1%80%D0%B5%D0%BA%D0%B2%D0%B5%D1%81%D1%82%D0%BE%D0%B2)
- [Мне нужно обновить свой форк последними изменениями из исходного репозитория](#%D0%9C%D0%BD%D0%B5-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE-%D0%BE%D0%B1%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C-%D1%81%D0%B2%D0%BE%D0%B9-%D1%84%D0%BE%D1%80%D0%BA-%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BD%D0%B8%D0%BC%D0%B8-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F%D0%BC%D0%B8-%D0%B8%D0%B7-%D0%B8%D1%81%D1%85%D0%BE%D0%B4%D0%BD%D0%BE%D0%B3%D0%BE-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F)
- [Редактирование коммитов](#%D0%A0%D0%B5%D0%B4%D0%B0%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%BE%D0%B2)
- [Что я только что сохранил?](#%D0%A7%D1%82%D0%BE-%D1%8F-%D1%82%D0%BE%D0%BB%D1%8C%D0%BA%D0%BE-%D1%87%D1%82%D0%BE-%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D0%BB)
- [Я неправильно написал сообщение коммита](#%D0%AF-%D0%BD%D0%B5%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D1%8C%D0%BD%D0%BE-%D0%BD%D0%B0%D0%BF%D0%B8%D1%81%D0%B0%D0%BB-%D1%81%D0%BE%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%B0)
- [Я сделал коммит с неправильным именем автора и адресом электронной почты](#%D0%AF-%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D0%BB-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82-%D1%81-%D0%BD%D0%B5%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D1%8C%D0%BD%D1%8B%D0%BC-%D0%B8%D0%BC%D0%B5%D0%BD%D0%B5%D0%BC-%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D0%B0-%D0%B8-%D0%B0%D0%B4%D1%80%D0%B5%D1%81%D0%BE%D0%BC-%D1%8D%D0%BB%D0%B5%D0%BA%D1%82%D1%80%D0%BE%D0%BD%D0%BD%D0%BE%D0%B9-%D0%BF%D0%BE%D1%87%D1%82%D1%8B)
- [Я хочу удалить файл из предыдущего коммита](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%83%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D1%84%D0%B0%D0%B9%D0%BB-%D0%B8%D0%B7-%D0%BF%D1%80%D0%B5%D0%B4%D1%8B%D0%B4%D1%83%D1%89%D0%B5%D0%B3%D0%BE-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%B0)
- [Я хочу удалить последний коммит](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%83%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BD%D0%B8%D0%B9-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82)
- [Удалить произвольный коммит](#%D0%A3%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82)
- [Я пытаюсь опубликовать исправленный коммит, но получаю сообщение об ошибке](#%D0%AF-%D0%BF%D1%8B%D1%82%D0%B0%D1%8E%D1%81%D1%8C-%D0%BE%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D0%B8%D1%81%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82-%D0%BD%D0%BE-%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B0%D1%8E-%D1%81%D0%BE%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%B8%D0%B5-%D0%BE%D0%B1-%D0%BE%D1%88%D0%B8%D0%B1%D0%BA%D0%B5)
- [Я случайно сделал жесткий сброс (--hard) и теперь хочу вернуть свои изменения](#%D0%AF-%D1%81%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D0%BE-%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D0%BB-%D0%B6%D0%B5%D1%81%D1%82%D0%BA%D0%B8%D0%B9-%D1%81%D0%B1%D1%80%D0%BE%D1%81---hard-%D0%B8-%D1%82%D0%B5%D0%BF%D0%B5%D1%80%D1%8C-%D1%85%D0%BE%D1%87%D1%83-%D0%B2%D0%B5%D1%80%D0%BD%D1%83%D1%82%D1%8C-%D1%81%D0%B2%D0%BE%D0%B8-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F)
- [Я случайно опубликовал ненужное слияние](#%D0%AF-%D1%81%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D0%BE-%D0%BE%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%BE%D0%B2%D0%B0%D0%BB-%D0%BD%D0%B5%D0%BD%D1%83%D0%B6%D0%BD%D0%BE%D0%B5-%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5)
- [Я случайно закоммитил и опубликовал файлы с конфиденциальными данными](#%D0%AF-%D1%81%D0%BB%D1%83%D1%87%D0%B0%D0%B9%D0%BD%D0%BE-%D0%B7%D0%B0%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%B8%D0%BB-%D0%B8-%D0%BE%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%BE%D0%B2%D0%B0%D0%BB-%D1%84%D0%B0%D0%B9%D0%BB%D1%8B-%D1%81-%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B4%D0%B5%D0%BD%D1%86%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%BC%D0%B8-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%BC%D0%B8)
- [Я хочу удалить большой файл из истории репозитория](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%83%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D0%B1%D0%BE%D0%BB%D1%8C%D1%88%D0%BE%D0%B9-%D1%84%D0%B0%D0%B9%D0%BB-%D0%B8%D0%B7-%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D0%B8-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F)
- [Рекомендуемый способ: Использование bfg](#%D0%A0%D0%B5%D0%BA%D0%BE%D0%BC%D0%B5%D0%BD%D0%B4%D1%83%D0%B5%D0%BC%D1%8B%D0%B9-%D1%81%D0%BF%D0%BE%D1%81%D0%BE%D0%B1-%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-bfg)
- [Встроенный способ: Использование git-filter-branch](#%D0%92%D1%81%D1%82%D1%80%D0%BE%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9-%D1%81%D0%BF%D0%BE%D1%81%D0%BE%D0%B1-%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-git-filter-branch)
- [Финальный шаг: Публикация измененной истории репозитория](#%D0%A4%D0%B8%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9-%D1%88%D0%B0%D0%B3-%D0%9F%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%BD%D0%BE%D0%B9-%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D0%B8-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F)
- [Мне нужно изменить содержимое коммита, который не является последним](#%D0%9C%D0%BD%D0%B5-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B8%D1%82%D1%8C-%D1%81%D0%BE%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%BC%D0%BE%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%B0-%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D0%B9-%D0%BD%D0%B5-%D1%8F%D0%B2%D0%BB%D1%8F%D0%B5%D1%82%D1%81%D1%8F-%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BD%D0%B8%D0%BC)
- [Подготовка изменений (staging)](#%D0%9F%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B0-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D0%B9-staging)
- [Мне нужно добавить подготовленные изменения в предыдущий коммит](#%D0%9C%D0%BD%D0%B5-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE-%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-%D0%BF%D1%80%D0%B5%D0%B4%D1%8B%D0%B4%D1%83%D1%89%D0%B8%D0%B9-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82)
- [Я хочу подготовить только часть файла, а не весь файл целиком](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D1%82%D1%8C-%D1%82%D0%BE%D0%BB%D1%8C%D0%BA%D0%BE-%D1%87%D0%B0%D1%81%D1%82%D1%8C-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0-%D0%B0-%D0%BD%D0%B5-%D0%B2%D0%B5%D1%81%D1%8C-%D1%84%D0%B0%D0%B9%D0%BB-%D1%86%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%BC)
- [Я хочу добавить изменения одного файла в два разных коммита](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%BE%D0%B4%D0%BD%D0%BE%D0%B3%D0%BE-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0-%D0%B2-%D0%B4%D0%B2%D0%B0-%D1%80%D0%B0%D0%B7%D0%BD%D1%8B%D1%85-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%B0)
- [Я подготовил слишком много правок и теперь хочу разделить их на несколько отдельных коммитов](#%D0%AF-%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D0%BB-%D1%81%D0%BB%D0%B8%D1%88%D0%BA%D0%BE%D0%BC-%D0%BC%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BF%D1%80%D0%B0%D0%B2%D0%BE%D0%BA-%D0%B8-%D1%82%D0%B5%D0%BF%D0%B5%D1%80%D1%8C-%D1%85%D0%BE%D1%87%D1%83-%D1%80%D0%B0%D0%B7%D0%B4%D0%B5%D0%BB%D0%B8%D1%82%D1%8C-%D0%B8%D1%85-%D0%BD%D0%B0-%D0%BD%D0%B5%D1%81%D0%BA%D0%BE%D0%BB%D1%8C%D0%BA%D0%BE-%D0%BE%D1%82%D0%B4%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D1%85-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%BE%D0%B2)
- [Я хочу подготовить свои неподготовленные правки и убрать из подготовки то, что уже подготовлено](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D1%82%D1%8C-%D1%81%D0%B2%D0%BE%D0%B8-%D0%BD%D0%B5%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BF%D1%80%D0%B0%D0%B2%D0%BA%D0%B8-%D0%B8-%D1%83%D0%B1%D1%80%D0%B0%D1%82%D1%8C-%D0%B8%D0%B7-%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B8-%D1%82%D0%BE-%D1%87%D1%82%D0%BE-%D1%83%D0%B6%D0%B5-%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BE)
- [Неподготовленные правки](#%D0%9D%D0%B5%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BF%D1%80%D0%B0%D0%B2%D0%BA%D0%B8)
- [Я хочу переместить мои неподготовленные правки в новую ветку](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C-%D0%BC%D0%BE%D0%B8-%D0%BD%D0%B5%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BF%D1%80%D0%B0%D0%B2%D0%BA%D0%B8-%D0%B2-%D0%BD%D0%BE%D0%B2%D1%83%D1%8E-%D0%B2%D0%B5%D1%82%D0%BA%D1%83)
- [Я хочу переместить неподготовленные правки в другую существующую ветку](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%B5%D1%80%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D0%B8%D1%82%D1%8C-%D0%BD%D0%B5%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BF%D1%80%D0%B0%D0%B2%D0%BA%D0%B8-%D0%B2-%D0%B4%D1%80%D1%83%D0%B3%D1%83%D1%8E-%D1%81%D1%83%D1%89%D0%B5%D1%81%D1%82%D0%B2%D1%83%D1%8E%D1%89%D1%83%D1%8E-%D0%B2%D0%B5%D1%82%D0%BA%D1%83)
- [Я хочу отменить мои локальные несохраненные изменения (подготовленные и неподготовленные)](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BE%D1%82%D0%BC%D0%B5%D0%BD%D0%B8%D1%82%D1%8C-%D0%BC%D0%BE%D0%B8-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%BD%D0%B5%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%B8-%D0%BD%D0%B5%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5)
- [Я хочу отменить некоторые неподготовленные изменения](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BE%D1%82%D0%BC%D0%B5%D0%BD%D0%B8%D1%82%D1%8C-%D0%BD%D0%B5%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D0%B5-%D0%BD%D0%B5%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F)
- [Я хочу отбросить неподготовленные изменения в некоторых файлах](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BE%D1%82%D0%B1%D1%80%D0%BE%D1%81%D0%B8%D1%82%D1%8C-%D0%BD%D0%B5%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-%D0%BD%D0%B5%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D1%85-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%D1%85)
- [Я хочу убрать все неподготовленные локальные изменения](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%83%D0%B1%D1%80%D0%B0%D1%82%D1%8C-%D0%B2%D1%81%D0%B5-%D0%BD%D0%B5%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F)
- [Я хочу удалить все неотслеживаемые файлы](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%83%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D0%B2%D1%81%D0%B5-%D0%BD%D0%B5%D0%BE%D1%82%D1%81%D0%BB%D0%B5%D0%B6%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC%D1%8B%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB%D1%8B)
- [Я хочу убрать заданный файл из подготовленного](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%83%D0%B1%D1%80%D0%B0%D1%82%D1%8C-%D0%B7%D0%B0%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9-%D1%84%D0%B0%D0%B9%D0%BB-%D0%B8%D0%B7-%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE)
- [Ветки](#%D0%92%D0%B5%D1%82%D0%BA%D0%B8)
- [Я хочу получить список всех веток](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D1%82%D1%8C-%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%B2%D1%81%D0%B5%D1%85-%D0%B2%D0%B5%D1%82%D0%BE%D0%BA)
- [Создать ветку на определенном коммите](#%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C-%D0%B2%D0%B5%D1%82%D0%BA%D1%83-%D0%BD%D0%B0-%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%BC-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%B5)
- [Я стянул изменения (pull) из неправильной ветки или в неправильную ветку](#%D0%AF-%D1%81%D1%82%D1%8F%D0%BD%D1%83%D0%BB-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-pull-%D0%B8%D0%B7-%D0%BD%D0%B5%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9-%D0%B2%D0%B5%D1%82%D0%BA%D0%B8-%D0%B8%D0%BB%D0%B8-%D0%B2-%D0%BD%D0%B5%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D1%8C%D0%BD%D1%83%D1%8E-%D0%B2%D0%B5%D1%82%D0%BA%D1%83)
- [Я хочу отменить локальные коммиты, чтобы моя ветка стала такой же как на сервере](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BE%D1%82%D0%BC%D0%B5%D0%BD%D0%B8%D1%82%D1%8C-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D1%8B-%D1%87%D1%82%D0%BE%D0%B1%D1%8B-%D0%BC%D0%BE%D1%8F-%D0%B2%D0%B5%D1%82%D0%BA%D0%B0-%D1%81%D1%82%D0%B0%D0%BB%D0%B0-%D1%82%D0%B0%D0%BA%D0%BE%D0%B9-%D0%B6%D0%B5-%D0%BA%D0%B0%D0%BA-%D0%BD%D0%B0-%D1%81%D0%B5%D1%80%D0%B2%D0%B5%D1%80%D0%B5)
- [Я сохранил коммит в ветку main вместо новой ветки](#%D0%AF-%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D0%BB-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82-%D0%B2-%D0%B2%D0%B5%D1%82%D0%BA%D1%83-main-%D0%B2%D0%BC%D0%B5%D1%81%D1%82%D0%BE-%D0%BD%D0%BE%D0%B2%D0%BE%D0%B9-%D0%B2%D0%B5%D1%82%D0%BA%D0%B8)
- [Я хочу сохранить файл целиком из другого ref-ish](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D1%82%D1%8C-%D1%84%D0%B0%D0%B9%D0%BB-%D1%86%D0%B5%D0%BB%D0%B8%D0%BA%D0%BE%D0%BC-%D0%B8%D0%B7-%D0%B4%D1%80%D1%83%D0%B3%D0%BE%D0%B3%D0%BE-ref-ish)
- [Я сделал несколько коммитов в одной ветке, а нужно было сохранять их в разных ветках](#%D0%AF-%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D0%BB-%D0%BD%D0%B5%D1%81%D0%BA%D0%BE%D0%BB%D1%8C%D0%BA%D0%BE-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%BE%D0%B2-%D0%B2-%D0%BE%D0%B4%D0%BD%D0%BE%D0%B9-%D0%B2%D0%B5%D1%82%D0%BA%D0%B5-%D0%B0-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE-%D0%B1%D1%8B%D0%BB%D0%BE-%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D1%8F%D1%82%D1%8C-%D0%B8%D1%85-%D0%B2-%D1%80%D0%B0%D0%B7%D0%BD%D1%8B%D1%85-%D0%B2%D0%B5%D1%82%D0%BA%D0%B0%D1%85)
- [Я хочу удалить локальные ветки, которые были удалены в upstream](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%83%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D0%B2%D0%B5%D1%82%D0%BA%D0%B8-%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D0%B5-%D0%B1%D1%8B%D0%BB%D0%B8-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D1%8B-%D0%B2-upstream)
- [Я нечаянно удалил мою ветку](#%D0%AF-%D0%BD%D0%B5%D1%87%D0%B0%D1%8F%D0%BD%D0%BD%D0%BE-%D1%83%D0%B4%D0%B0%D0%BB%D0%B8%D0%BB-%D0%BC%D0%BE%D1%8E-%D0%B2%D0%B5%D1%82%D0%BA%D1%83)
- [Я хочу удалить ветку](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%83%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D0%B2%D0%B5%D1%82%D0%BA%D1%83)
- [Я хочу удалить несколько веток](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%83%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D0%BD%D0%B5%D1%81%D0%BA%D0%BE%D0%BB%D1%8C%D0%BA%D0%BE-%D0%B2%D0%B5%D1%82%D0%BE%D0%BA)
- [Я хочу переименовать ветку](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%B5%D1%80%D0%B5%D0%B8%D0%BC%D0%B5%D0%BD%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D0%B2%D0%B5%D1%82%D0%BA%D1%83)
- [Я хочу перейти на удаленную ветку, над которой работает кто-то еще](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%B5%D1%80%D0%B5%D0%B9%D1%82%D0%B8-%D0%BD%D0%B0-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%BD%D1%83%D1%8E-%D0%B2%D0%B5%D1%82%D0%BA%D1%83-%D0%BD%D0%B0%D0%B4-%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D0%BE%D0%B9-%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%B0%D0%B5%D1%82-%D0%BA%D1%82%D0%BE-%D1%82%D0%BE-%D0%B5%D1%89%D0%B5)
- [Я хочу создать новую удаленную ветку из текущей локальной](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C-%D0%BD%D0%BE%D0%B2%D1%83%D1%8E-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%BD%D1%83%D1%8E-%D0%B2%D0%B5%D1%82%D0%BA%D1%83-%D0%B8%D0%B7-%D1%82%D0%B5%D0%BA%D1%83%D1%89%D0%B5%D0%B9-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9)
- [Я хочу настроить локальную ветку на отслеживание удаленной (upstream) ветки](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B8%D1%82%D1%8C-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D1%83%D1%8E-%D0%B2%D0%B5%D1%82%D0%BA%D1%83-%D0%BD%D0%B0-%D0%BE%D1%82%D1%81%D0%BB%D0%B5%D0%B6%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B9-upstream-%D0%B2%D0%B5%D1%82%D0%BA%D0%B8)
- [Я хочу настроить HEAD на отслеживание основной удаленной ветки](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B8%D1%82%D1%8C-head-%D0%BD%D0%B0-%D0%BE%D1%82%D1%81%D0%BB%D0%B5%D0%B6%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BE%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%BE%D0%B9-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%B9-%D0%B2%D0%B5%D1%82%D0%BA%D0%B8)
- [Я сделал изменения в неправильной ветке](#%D0%AF-%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D0%BB-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-%D0%BD%D0%B5%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9-%D0%B2%D0%B5%D1%82%D0%BA%D0%B5)
- [Перебазирование (rebase) и слияние (merge)](#%D0%9F%D0%B5%D1%80%D0%B5%D0%B1%D0%B0%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-rebase-%D0%B8-%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5-merge)
- [Я хочу отменить перебазирование/слияние](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BE%D1%82%D0%BC%D0%B5%D0%BD%D0%B8%D1%82%D1%8C-%D0%BF%D0%B5%D1%80%D0%B5%D0%B1%D0%B0%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5)
- [Я сделал перебазирование, но я не хочу делать принудительный push](#%D0%AF-%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D0%BB-%D0%BF%D0%B5%D1%80%D0%B5%D0%B1%D0%B0%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BD%D0%BE-%D1%8F-%D0%BD%D0%B5-%D1%85%D0%BE%D1%87%D1%83-%D0%B4%D0%B5%D0%BB%D0%B0%D1%82%D1%8C-%D0%BF%D1%80%D0%B8%D0%BD%D1%83%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9-push)
- [Я хочу объединить коммиты](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BE%D0%B1%D1%8A%D0%B5%D0%B4%D0%B8%D0%BD%D0%B8%D1%82%D1%8C-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D1%8B)
- [Безопасная стратегия слияния](#%D0%91%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D0%B0%D1%8F-%D1%81%D1%82%D1%80%D0%B0%D1%82%D0%B5%D0%B3%D0%B8%D1%8F-%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D1%8F)
- [Мне нужно слить ветку в единственный коммит](#%D0%9C%D0%BD%D0%B5-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE-%D1%81%D0%BB%D0%B8%D1%82%D1%8C-%D0%B2%D0%B5%D1%82%D0%BA%D1%83-%D0%B2-%D0%B5%D0%B4%D0%B8%D0%BD%D1%81%D1%82%D0%B2%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82)
- [Я хочу объединить только неопубликованные коммиты](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BE%D0%B1%D1%8A%D0%B5%D0%B4%D0%B8%D0%BD%D0%B8%D1%82%D1%8C-%D1%82%D0%BE%D0%BB%D1%8C%D0%BA%D0%BE-%D0%BD%D0%B5%D0%BE%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D1%8B)
- [Мне нужно прервать слияние](#%D0%9C%D0%BD%D0%B5-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE-%D0%BF%D1%80%D0%B5%D1%80%D0%B2%D0%B0%D1%82%D1%8C-%D1%81%D0%BB%D0%B8%D1%8F%D0%BD%D0%B8%D0%B5)
- [Мне нужно обновить родительский коммит моей ветки](#%D0%9C%D0%BD%D0%B5-%D0%BD%D1%83%D0%B6%D0%BD%D0%BE-%D0%BE%D0%B1%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C-%D1%80%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D1%81%D0%BA%D0%B8%D0%B9-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82-%D0%BC%D0%BE%D0%B5%D0%B9-%D0%B2%D0%B5%D1%82%D0%BA%D0%B8)
- [Проверить, что все коммиты ветви были слиты](#%D0%9F%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D0%B8%D1%82%D1%8C-%D1%87%D1%82%D0%BE-%D0%B2%D1%81%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D1%8B-%D0%B2%D0%B5%D1%82%D0%B2%D0%B8-%D0%B1%D1%8B%D0%BB%D0%B8-%D1%81%D0%BB%D0%B8%D1%82%D1%8B)
- [Возможные проблемы интерактивного перебазирования](#%D0%92%D0%BE%D0%B7%D0%BC%D0%BE%D0%B6%D0%BD%D1%8B%D0%B5-%D0%BF%D1%80%D0%BE%D0%B1%D0%BB%D0%B5%D0%BC%D1%8B-%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%B0%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BF%D0%B5%D1%80%D0%B5%D0%B1%D0%B0%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)
- [Экран перебазирования говорит 'noop'](#%D0%AD%D0%BA%D1%80%D0%B0%D0%BD-%D0%BF%D0%B5%D1%80%D0%B5%D0%B1%D0%B0%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F-%D0%B3%D0%BE%D0%B2%D0%BE%D1%80%D0%B8%D1%82-noop)
- [Здесь были конфликты](#%D0%97%D0%B4%D0%B5%D1%81%D1%8C-%D0%B1%D1%8B%D0%BB%D0%B8-%D0%BA%D0%BE%D0%BD%D1%84%D0%BB%D0%B8%D0%BA%D1%82%D1%8B)
- [Отложенные изменения (stash)](#%D0%9E%D1%82%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-stash)
- [Отложить все правки](#%D0%9E%D1%82%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C-%D0%B2%D1%81%D0%B5-%D0%BF%D1%80%D0%B0%D0%B2%D0%BA%D0%B8)
- [Отложить заданные файлы](#%D0%9E%D1%82%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C-%D0%B7%D0%B0%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB%D1%8B)
- [Отложить с сообщением](#%D0%9E%D1%82%D0%BB%D0%BE%D0%B6%D0%B8%D1%82%D1%8C-%D1%81-%D1%81%D0%BE%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%B8%D0%B5%D0%BC)
- [Применить заданный stash из списка](#%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D0%B8%D1%82%D1%8C-%D0%B7%D0%B0%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9-stash-%D0%B8%D0%B7-%D1%81%D0%BF%D0%B8%D1%81%D0%BA%D0%B0)
- [Поиск](#%D0%9F%D0%BE%D0%B8%D1%81%D0%BA)
- [Я хочу найти строку в коммитах](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BD%D0%B0%D0%B9%D1%82%D0%B8-%D1%81%D1%82%D1%80%D0%BE%D0%BA%D1%83-%D0%B2-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%B0%D1%85)
- [Я хочу искать по автору или сохранившему изменения (committer)](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%B8%D1%81%D0%BA%D0%B0%D1%82%D1%8C-%D0%BF%D0%BE-%D0%B0%D0%B2%D1%82%D0%BE%D1%80%D1%83-%D0%B8%D0%BB%D0%B8-%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D0%B2%D1%88%D0%B5%D0%BC%D1%83-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-committer)
- [Я хочу получить список коммитов, содержащих заданный файл](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D1%82%D1%8C-%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%BE%D0%B2-%D1%81%D0%BE%D0%B4%D0%B5%D1%80%D0%B6%D0%B0%D1%89%D0%B8%D1%85-%D0%B7%D0%B0%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B9-%D1%84%D0%B0%D0%B9%D0%BB)
- [Я хочу посмотреть историю коммитов для отдельной функции](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%BE%D1%81%D0%BC%D0%BE%D1%82%D1%80%D0%B5%D1%82%D1%8C-%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D1%8E-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%BE%D0%B2-%D0%B4%D0%BB%D1%8F-%D0%BE%D1%82%D0%B4%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8)
- [Найти метки для заданного коммита](#%D0%9D%D0%B0%D0%B9%D1%82%D0%B8-%D0%BC%D0%B5%D1%82%D0%BA%D0%B8-%D0%B4%D0%BB%D1%8F-%D0%B7%D0%B0%D0%B4%D0%B0%D0%BD%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%B0)
- [Субмодули](#%D0%A1%D1%83%D0%B1%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D0%B8)
- [Клонировать все субмодули](#%D0%9A%D0%BB%D0%BE%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D0%B2%D1%81%D0%B5-%D1%81%D1%83%D0%B1%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D0%B8)
- [Удалить субмодуль](#%D0%A3%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D1%81%D1%83%D0%B1%D0%BC%D0%BE%D0%B4%D1%83%D0%BB%D1%8C)
- [Разное](#%D0%A0%D0%B0%D0%B7%D0%BD%D0%BE%D0%B5)
- [Восстановить удаленный файл](#%D0%92%D0%BE%D1%81%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9-%D1%84%D0%B0%D0%B9%D0%BB)
- [Удалить метку](#%D0%A3%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D0%BC%D0%B5%D1%82%D0%BA%D1%83)
- [Восстановить удаленную метку](#%D0%92%D0%BE%D1%81%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%B8%D1%82%D1%8C-%D1%83%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%BD%D1%83%D1%8E-%D0%BC%D0%B5%D1%82%D0%BA%D1%83)
- [Удаленный патч](#%D0%A3%D0%B4%D0%B0%D0%BB%D0%B5%D0%BD%D0%BD%D1%8B%D0%B9-%D0%BF%D0%B0%D1%82%D1%87)
- [Экспорт репозитория в Zip-файл](#%D0%AD%D0%BA%D1%81%D0%BF%D0%BE%D1%80%D1%82-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F-%D0%B2-zip-%D1%84%D0%B0%D0%B9%D0%BB)
- [Как опубликовать ветку и метку, если они имеют одинаковые имена](#%D0%9A%D0%B0%D0%BA-%D0%BE%D0%BF%D1%83%D0%B1%D0%BB%D0%B8%D0%BA%D0%BE%D0%B2%D0%B0%D1%82%D1%8C-%D0%B2%D0%B5%D1%82%D0%BA%D1%83-%D0%B8-%D0%BC%D0%B5%D1%82%D0%BA%D1%83-%D0%B5%D1%81%D0%BB%D0%B8-%D0%BE%D0%BD%D0%B8-%D0%B8%D0%BC%D0%B5%D1%8E%D1%82-%D0%BE%D0%B4%D0%B8%D0%BD%D0%B0%D0%BA%D0%BE%D0%B2%D1%8B%D0%B5-%D0%B8%D0%BC%D0%B5%D0%BD%D0%B0)
- [Отслеживание файлов](#%D0%9E%D1%82%D1%81%D0%BB%D0%B5%D0%B6%D0%B8%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2)
- [Я хочу изменить регистр в имени файла, не меняя содержимое файла](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B8%D1%82%D1%8C-%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80-%D0%B2-%D0%B8%D0%BC%D0%B5%D0%BD%D0%B8-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0-%D0%BD%D0%B5-%D0%BC%D0%B5%D0%BD%D1%8F%D1%8F-%D1%81%D0%BE%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%BC%D0%BE%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0)
- [Я хочу переписать локальные файлы при выполнении git pull](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%B5%D1%80%D0%B5%D0%BF%D0%B8%D1%81%D0%B0%D1%82%D1%8C-%D0%BB%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5-%D1%84%D0%B0%D0%B9%D0%BB%D1%8B-%D0%BF%D1%80%D0%B8-%D0%B2%D1%8B%D0%BF%D0%BE%D0%BB%D0%BD%D0%B5%D0%BD%D0%B8%D0%B8-git-pull)
- [Я хочу удалить файл из git, но оставить сам файл](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%83%D0%B4%D0%B0%D0%BB%D0%B8%D1%82%D1%8C-%D1%84%D0%B0%D0%B9%D0%BB-%D0%B8%D0%B7-git-%D0%BD%D0%BE-%D0%BE%D1%81%D1%82%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D1%81%D0%B0%D0%BC-%D1%84%D0%B0%D0%B9%D0%BB)
- [Я хочу откатить файл до заданной ревизии](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BE%D1%82%D0%BA%D0%B0%D1%82%D0%B8%D1%82%D1%8C-%D1%84%D0%B0%D0%B9%D0%BB-%D0%B4%D0%BE-%D0%B7%D0%B0%D0%B4%D0%B0%D0%BD%D0%BD%D0%BE%D0%B9-%D1%80%D0%B5%D0%B2%D0%B8%D0%B7%D0%B8%D0%B8)
- [Я хочу получить список изменений в заданном файле из разных коммитов или веток](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%BF%D0%BE%D0%BB%D1%83%D1%87%D0%B8%D1%82%D1%8C-%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D0%B9-%D0%B2-%D0%B7%D0%B0%D0%B4%D0%B0%D0%BD%D0%BD%D0%BE%D0%BC-%D1%84%D0%B0%D0%B9%D0%BB%D0%B5-%D0%B8%D0%B7-%D1%80%D0%B0%D0%B7%D0%BD%D1%8B%D1%85-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B8%D1%82%D0%BE%D0%B2-%D0%B8%D0%BB%D0%B8-%D0%B2%D0%B5%D1%82%D0%BE%D0%BA)
- [Я хочу, чтобы Git игнорировал изменения в определенном файле](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%87%D1%82%D0%BE%D0%B1%D1%8B-git-%D0%B8%D0%B3%D0%BD%D0%BE%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BB-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B2-%D0%BE%D0%BF%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D0%B5%D0%BD%D0%BD%D0%BE%D0%BC-%D1%84%D0%B0%D0%B9%D0%BB%D0%B5)
- [Отладка с помощью Git](#%D0%9E%D1%82%D0%BB%D0%B0%D0%B4%D0%BA%D0%B0-%D1%81-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C%D1%8E-git)
- [Конфигурация](#%D0%9A%D0%BE%D0%BD%D1%84%D0%B8%D0%B3%D1%83%D1%80%D0%B0%D1%86%D0%B8%D1%8F)
- [Я хочу добавить псевдонимы для некоторых команд Git](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D0%BF%D1%81%D0%B5%D0%B2%D0%B4%D0%BE%D0%BD%D0%B8%D0%BC%D1%8B-%D0%B4%D0%BB%D1%8F-%D0%BD%D0%B5%D0%BA%D0%BE%D1%82%D0%BE%D1%80%D1%8B%D1%85-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4-git)
- [Я хочу добавить в свой репозиторий пустую папку](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D0%B2-%D1%81%D0%B2%D0%BE%D0%B9-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9-%D0%BF%D1%83%D1%81%D1%82%D1%83%D1%8E-%D0%BF%D0%B0%D0%BF%D0%BA%D1%83)
- [Я хочу сохранить имя пользователя и пароль для репозитория](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%81%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D1%82%D1%8C-%D0%B8%D0%BC%D1%8F-%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8F-%D0%B8-%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C-%D0%B4%D0%BB%D1%8F-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F)
- [Я хочу, чтобы Git игнорировал изменения разрешений и прав файлов](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D1%87%D1%82%D0%BE%D0%B1%D1%8B-git-%D0%B8%D0%B3%D0%BD%D0%BE%D1%80%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BB-%D0%B8%D0%B7%D0%BC%D0%B5%D0%BD%D0%B5%D0%BD%D0%B8%D1%8F-%D1%80%D0%B0%D0%B7%D1%80%D0%B5%D1%88%D0%B5%D0%BD%D0%B8%D0%B9-%D0%B8-%D0%BF%D1%80%D0%B0%D0%B2-%D1%84%D0%B0%D0%B9%D0%BB%D0%BE%D0%B2)
- [Я хочу задать данные пользователя в глобальных настройках](#%D0%AF-%D1%85%D0%BE%D1%87%D1%83-%D0%B7%D0%B0%D0%B4%D0%B0%D1%82%D1%8C-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8F-%D0%B2-%D0%B3%D0%BB%D0%BE%D0%B1%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D1%85-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0%D1%85)
- [Я не представляю что я сделал неправильно](#%D0%AF-%D0%BD%D0%B5-%D0%BF%D1%80%D0%B5%D0%B4%D1%81%D1%82%D0%B0%D0%B2%D0%BB%D1%8F%D1%8E-%D1%87%D1%82%D0%BE-%D1%8F-%D1%81%D0%B4%D0%B5%D0%BB%D0%B0%D0%BB-%D0%BD%D0%B5%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D1%8C%D0%BD%D0%BE)
- [Сокращения для команд Git](#%D0%A1%D0%BE%D0%BA%D1%80%D0%B0%D1%89%D0%B5%D0%BD%D0%B8%D1%8F-%D0%B4%D0%BB%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4-git)
- [Git Bash](#git-bash)
- [PowerShell в Windows](#powershell-%D0%B2-windows)
- [Другие ресурсы](#%D0%94%D1%80%D1%83%D0%B3%D0%B8%D0%B5-%D1%80%D0%B5%D1%81%D1%83%D1%80%D1%81%D1%8B)
- [Книги](#%D0%9A%D0%BD%D0%B8%D0%B3%D0%B8)
- [Учебники](#%D0%A3%D1%87%D0%B5%D0%B1%D0%BD%D0%B8%D0%BA%D0%B8)
- [Скрипты и инструменты](#%D0%A1%D0%BA%D1%80%D0%B8%D0%BF%D1%82%D1%8B-%D0%B8-%D0%B8%D0%BD%D1%81%D1%82%D1%80%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D1%8B)
- [Графические клиенты](#%D0%93%D1%80%D0%B0%D1%84%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5-%D0%BA%D0%BB%D0%B8%D0%B5%D0%BD%D1%82%D1%8B)
## Репозитории
### Я хочу создать локальный репозиторий
Чтобы создать репозиторий Git в текущей папке:
```sh
(my-folder) $ git init
```
### Я хочу клонировать удаленный репозиторий
Чтобы клонировать (копировать) удаленный репозиторий, скопируйте URL репозитория и запустите:
```sh
$ git clone [url]
```
Git сохранит копию в папке с названием репозитория. Убедитесь, что у Вас есть соединение с удаленным сервером, откуда Вы клонируете (в большинстве случаев это означает наличие выхода в Интернет).
Чтобы клонировать в папку с произвольным именем:
```sh
$ git clone [url] name-of-new-folder
```
### Я неправильно задал удаленный репозиторий
Вот несколько возможных проблем:
Если Вы склонировали неправильный репозиторий, то просто удалите появившуюся папку и склонируйте правильный репозиторий.
Если Вы неправильно настроили origin для существующего локального репозиторий, то измените URL-адрес origin на правильный с помощью:
```sh
$ git remote set-url origin [правильный url]
```
См. [подробнее на StackOverflow](https://stackoverflow.com/questions/2432764/how-to-change-the-uri-url-for-a-remote-git-repository#2432799).
### Я хочу добавить код в чужой репозиторий
Git не разрешает добавлять код в чужой репозиторий без соответствующих прав доступа. Так же, как и GitHub, который, вообще говоря, не является самим Git-ом, а скорее хостингом для репозиториев Git. Тем не менее, вы можете предложить код посредством патчей или, на GitHub, посредством форков (ответвлений) и пулл-реквестов (запросов на слияние).
Сначала немного о форках. Форк - это копия репозитория. Эта операция не имеет прямого отношения к Git, это общая операция на GitHub, Bitbucket, GitLab и на других хостингах репозиториев Git. Вы можете сделать форк с помощью веб-интерфейса.
#### Предложение кода с помощью пулл-реквестов
После создания форка вам нужно склонировать репозиторий на свой компьютер. Вы можете сделать мелкие правки прямо на GitHub без клонирования, но это не "Правила полёта на GitHub", так что рассмотрим, как это сделать у себя на компьютере.
```sh
# если вы используете SSH
$ git clone git@github.com:k88hudson/git-flight-rules.git
# если вы используете HTTPS
$ git clone https://github.com/k88hudson/git-flight-rules.git
```
Если перейти в появившийся каталог и ввести команду `git remote`, то напечатается список ссылок на удаленные репозитории. Обычно будет единственная ссылка `origin`, которая будет указывать на `k88hudson/git-flight-rules`. Ещё вам понадобится ссылка на ваш форк.
Следуя общепринятым соглашениям, название `origin` используется для вашего собственного репозитория, а `upstream` - для исходного репозитория, с которого вы сделали форк. Так что переименуем `origin` в `upstream`
```sh
$ git remote rename origin upstream
```
Вместо этого можно использовать `git remote set-url`, но это дольше и требует больше действий.
Теперь настроим удаленную ссылку на ваш собственный репозиторий.
```sh
$ git remote add origin git@github.com:YourName/git-flight-rules.git
```
Обратите внимание, что у вас два удаленных репозитория.
- `origin` ссылается на ваш собственный репозиторий.
- `upstream` ссылается на исходный репозиторий.
Репозиторий origin доступен для чтения и записи, тогда как upstream доступен только для чтения.
Когда вы сделаете изменения, которые хотели, отправьте свои изменения (которые обычно делаются в отдельной ветке) в репозиторий `origin`. Если вы делаете изменения в отдельной ветке, то удобно пользоваться опцией `--set-upstream`, чтобы в будущем при отправке данной локальной ветки не приходилось каждый раз указывать удаленную ветку. Например:
```sh
$ (feature/my-feature) git push --set-upstream origin feature/my-feature
```
Git не располагает командами для создания пулл-реквестов (хотя есть внешние инструменты вроде [hub](http://github.com/github/hub)). Так что, когда подготовите необходимые изменения, открывайте GitHub (или другой сервер Git) и создавайте пулл-реквест. Обратите внимание, что сервер автоматически связывает исходный репозиторий и форк.
После этого не забудьте добавить описание предлагаемых изменений.
#### Мне нужно обновить свой форк последними изменениями из исходного репозитория
Со временем репозиторий `upstream` может обновиться, и эти обновления нужно внести в ваш репозиторий `origin`. Не забывайте, что другие люди тоже вносят свои изменения. Предположим, вы находитесь на ветке для своей фичи и вам нужно её обновить согласно последним изменениям исходного репозитория.
Возможно, вы уже настроили ссылку на исходный удаленный репозиторий. Если нет, тогда сделайте это сейчас. Обычно исходный удаленный репозиторий называют `upstream`:
```sh
$ (main) git remote add upstream
# $ (main) git remote add upstream git@github.com:k88hudson/git-flight-rules.git
```
Теперь вы можете получить последние обновления из upstream.
```sh
$ (main) git fetch upstream
$ (main) git merge upstream/main
# или одной командой
$ (main) git pull upstream main
```
## Редактирование коммитов
### Что я только что сохранил?
Допустим, Вы не глядя сохранили изменения с помощью `git commit -a` и теперь не уверены что именно сохранили. В таком случае Вы можете просмотреть последний коммит в HEAD с помощью:
```sh
(main)$ git show
```
Или
```sh
$ git log -n1 -p
```
Если Вы хотите просмотреть файл из определенного коммита, Вы можете сделать так (`` - нужный Вам коммит):
```sh
$ git show :filename
```
### Я неправильно написал сообщение коммита
Если Вы неправильно сохранили коммит, но еще не сделали `push`, то для исправления сообщения коммита сделайте следующее:
```sh
$ git commit --amend --only
```
Это откроет текстовый редактор по-умолчанию, в котором Вы сможете исправить сообщение. С другой стороны Вы можете сделать это одной командой:
```sh
$ git commit --amend --only -m 'xxxxxxx'
```
Если Вы уже сделали `push`, то Вы по-прежнему можете исправить коммит, но после этого придется делать `push` с принудительной перезаписью, что не рекомендуется.
### Я сделал коммит с неправильным именем автора и адресом электронной почты
Если это один коммит, то исправьте его с помощью `amend`
```sh
$ git commit --amend --no-edit --author "New Authorname "
```
Или Вы можете сконфигурировать глобальные настройки автора `git config --global author.(name|email)`, а затем выполнить
```sh
$ git commit --amend --reset-author --no-edit
```
Если Вам нужно изменить всю историю, то смотрите документацию для `git filter-branch`.
### Я хочу удалить файл из предыдущего коммита
Чтобы удалить изменения файла из предыдущего коммита, сделайте следующее:
```sh
$ git checkout HEAD^ myfile
$ git add myfile
$ git commit --amend --no-edit
```
Когда в коммит был добавлен новый файл и Вы хотите его удалить (только из Git), выполните:
```sh
$ git rm --cached myfile
$ git commit --amend --no-edit
```
Это особенно полезно, когда у Вас открытый патч, а Вы сохранили ненужный файл и теперь нужно сделать принудительный `push` для обновления патча в удаленном репозитории. Опция `--no-edit` оставляет прежнее сообщение коммита без изменений.
### Я хочу удалить последний коммит
Если хотите удалить опубликованные коммиты, воспользуйтесь приведенным ниже приемом. Однако, это бесповоротно изменит у Вас историю Git, а также испортит историю Git у любого, что уже стянул (pull) изменения из репозитория. Короче говоря, никогда так не делайте, если не уверены.
```sh
$ git reset HEAD^ --hard
$ git push --force-with-lease [remote] [branch]
```
Если Вы еще не опубликовали коммит, то просто сбросьте ветку в состояние перед Вашим последним коммитом (подготовленные изменения не пропадут):
```
(my-branch)$ git reset --soft HEAD^
```
Это работает, если Вы еще не сделали `push`. Если Вы уже сделали `push`, то единственный по-настоящему безопасный способ это `git revert SHAofBadCommit`. Это создаст новый коммит, который отменит все изменения предыдущего коммита. Или, если ветка, в которую вы делаете `push` безопасна для перезаписи (т.е. не предполагается, другие разработчики будут стягивать из нее изменения), то просто используйте `git push --force-with-lease`. Подробнее см. [в этом пункте выше](#delete-pushed-commit).
### Удалить произвольный коммит
Здесь уместны те же предупреждения, что и в пункте выше. По возможности, никогда так не делайте.
```sh
$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push --force-with-lease [remote] [branch]
```
Или сделайте [интерактивное перебазирование](#interactive-rebase) и удалите строки ненужных коммитов.
### Я пытаюсь опубликовать исправленный коммит, но получаю сообщение об ошибке
```sh
To https://github.com/yourusername/repo.git
! [rejected] mybranch -> mybranch (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```
Напомним, что подобно перебазированию (см. ниже), исправление коммита (amend) **заменяет старый коммит новым**, поэтому Вы должны делать принудительный `push` (`--force-with-lease`) Ваших изменений, если хотите заменить уже опубликованные на удаленном репозитории коммиты. Будьте осторожны, когда так делаете – *всегда* проверяйте с какой веткой Вы проводите эти действия!
```sh
(my-branch)$ git push origin mybranch --force-with-lease
```
В общем, **избегайте делать принудительный push**. Лучше создать и опубликовать еще один коммит, чем переписывать измененные коммиты, т.к. это приведет к конфликтам истории у других разработчиков, которые работают с этой или дочерними ветками. `--force-with-lease` по-прежнему выдаст ошибку, если кто-то одновременно с Вами работает с данной веткой и Ваш принудительный push переписал бы чужие изменения.
Если Вы *абсолютно* уверены, что никто кроме Вас не работает с данной веткой или Вы хотите обновить вершину ветви в любом случае, то используйте `--force` (`-f`), но вообще этого следует избегать.
### Я случайно сделал жесткий сброс (--hard) и теперь хочу вернуть свои изменения
Если Вы случайно сделали `git reset --hard`, то вы можете вернуть назад коммиты, т.к. Git несколько дней хранит все действия в журнале.
Замечание: Это относится только если ваша работа была сохранена, т.е. Вы сделали коммит или stash. `git reset --hard` *удалит* несохраненные изменения, так что пользуйтесь им с осторожностью. (Безопасная опция это `git reset --keep`.)
```sh
(main)$ git reflog
```
Вы увидете список Ваших предыдущих коммитов и коммит для сброса. Выберите SHA коммита, который хотите вернуть и снова сделайте сброс:
```sh
(main)$ git reset --hard SHA1234
```
И Вы сможете продолжить работу.
### Я случайно опубликовал ненужное слияние
Если Вы случайно сделали слияние в основную ветку разработки до того, как были сделаны все необходимые изменения, Вы по-прежнему можете отменить слияние. Но есть одна загвоздка: Коммит слияния имеет более одного родителя (обычно два).
Команда для решения этой проблемы
```sh
(feature-branch)$ git revert -m 1
```
где опция -m 1 говорит выбрать родителя номер 1 (ветка, в которую было сделано слияние) в качестве родителя для отката.
Заметка: номер родителя - это не идентификатор коммита. Допустим, коммит слияния имеет строчку `Merge: 8e2ce2d 86ac2e7`. Номер родителя - это порядковый номер (отсчет с 1) нужного родителя в этой строчке, первый идентификатор - номер 1, второй - номер 2 и т.д.
### Я случайно закоммитил и опубликовал файлы с конфиденциальными данными
Если Вы случайно опубликовали файлы, содержащие конфиденциальные данные (пароли, ключи и пр.), Вы можете изменить последний коммит с помощью amend. Помните, что как только Вы опубликовали коммит, то Вы должны считать всё его содержание скомпрометированным. С помощью дальнейших шагов можно удалить конфиденциальную информацию из публичного репозиторий или из Вашей локальной копии, вы **не сможете** удалить свою конфиденциальную информацию у людей, которые могли успеть скопировать себе её. Если Вы закоммитили пароль, то **незамедлительно поменяйте его**. Если Вы закоммитили ключ, то **незамедлительно сгенерируйте новый**. Изменения опубликованного коммита недостаточно, потому что кто-нибудь мог скопировать исходный коммит до того, как Вы успели его исправить.
Как только удалите конфиденциальные данные из файла, запустите
```sh
(feature-branch)$ git add edited_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
Если Вы хотите удалить файл целиком, но оставить его в локальной копии, то запустите
```sh
(feature-branch)$ git rm --cached sensitive_file
echo sensitive_file >> .gitignore
(feature-branch)$ git add .gitignore
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
Другим способом хранения конфиденциальных данных является хранение в переменных окружения.
Если Вы хотите удалить файл целиком и не оставлять его в локальной копии, то запустите
```sh
(feature-branch)$ git rm sensitive_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
Если Вы успели сделать другие коммиты после коммита с конфиденциальными данными, то Вам нужно использовать rebase.
### Я хочу удалить большой файл из истории репозитория
Если вы хотите удалить пароль или другую конфиденциальную информацию, то вместо этого смотрите [Я случайно закоммитил и опубликовал файлы с конфиденциальными данными](#undo-sensitive-commit-push).
Даже если вы удалите большие или ненужные файлы из последнего коммита, они останутся в истоии Git, в подкаталоге `.git` вашего репозитория и `git clone` будет загружать ненужные файлы.
Действия, описанные в этой части руководства требуют принудительной перезаписи истории репозитория Git, так что, если вы сотрудничаете с удаленными разработчиками, что сначала убедитесь, что они опубликовали на сервере все свои локальные изменения.
Есть два способа переписывания истории: встроенная команда `git-filter-branch` и сторонняя программа [`bfg-repo-cleaner`](https://rtyley.github.io/bfg-repo-cleaner/). `bfg` значительно понятнее в использовании и быстрее по производительности, но её нужно устанавливать отдельно и она требует Java. Мы опишем оба этих способа. Финальный шаг - это принудительный push ваших изменений, который мы обсудим отдельно, поскольку, в отличие от обычного принудительного push, он приводит к масштабной безвозвратной перезаписи истории.
#### Рекомендуемый способ: Использование bfg
Использование bfg-repo-cleaner требует наличия Java. Скачайте bfg jar-файл по ссылке [здесь](https://rtyley.github.io/bfg-repo-cleaner/). В наших примерах будет использоваться `bfg.jar`, а загруженный вами файл будет содержать в названии номер версии, например, `bfg-1.13.0.jar`.
Чтобы удалить определенный файл:
```sh
(main)$ git rm path/to/filetoremove
(main)$ git commit -m "Commit removing filetoremove"
(main)$ java -jar ~/Downloads/bfg.jar --delete-files filetoremove
```
Заметьте, что в bfg вы должны использовать имя файла без пути, даже если файл находится в подкаталоге.
Также вы можете пользоваться шаблонами имен:
```sh
(main)$ git rm *.jpg
(main)$ git commit -m "Commit removing *.jpg"
(main)$ java -jar ~/Downloads/bfg.jar --delete-files *.jpg
```
При использовании bfg, файлы, существующие в вашем последнем коммите не будут затронуты. Например, если в вашем репозитории было много больших .tga файлов, а потом вы удалили несколько из них, то bfg не тронет файлы, которые присутствуют в последнем коммите.
Заметьте, что если вы переименовали файл в коммите, например, если он сначала назывался `LargeFileFirstName.mp4` и коммит переименовал его в `LargeFileSecondName.mp4`, то запуск `java -jar ~/Downloads/bfg.jar --delete-files LargeFileSecondName.mp4` не удалит его из истории Git. В таком случае запустите команду `--delete-files` с обоими именами или с шаблоном имен.
#### Встроенный способ: Использование git-filter-branch
Команду `git-filter-branch` сложнее использовать и она имеет меньше возможностей, но вы можете ей воспользоваться, если не хотите пользоваться `bfg`.
В приведенной ниже команде замените `filepattern` на требуемое имя или шаблон, например, `*.jpg`. Эта команда удалит файлы, подходящие под заданный шаблон из истории всех ветвей.
```sh
(main)$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch filepattern' --prune-empty --tag-name-filter cat -- --all
```
Пояснения:
`--tag-name-filter cat` - это неуклюжий, но в то же время самый простой способ применить исходные метки к новым коммитам с помощью команды cat.
`--prune-empty` удаляет все коммиты, которые теперь стали пустыми.
#### Финальный шаг: Публикация измененной истории репозитория
Как только вы удалили файлы, которые хотели, внимально проверьте, что ничего не сломали в репозитории - если сломали, то проще всего склонировать ваш репозиторий и начать снова.
Для завершения вы можете опционально запустить сборщик мусора Git, чтобы минимизировать размер локальной папки .git folder size, а после этого сделать принудительный push.
```sh
(main)$ git reflog expire --expire=now --all && git gc --prune=now --aggressive
(main)$ git push origin --force --tags
```
После того, как вы переписали всю историю репозитория, операция `git push` может оказаться слишком большой и вернет ошибку `“The remote end hung up unexpectedly” (Удаленный хост неожиданно прервал соединение)`. Если это произошло, то попробуйте увеличить размер буфера передачи:
```sh
(main)$ git config http.postBuffer 524288000
(main)$ git push --force
```
Если это не работает, то вам придется делать push частями. В команде ниже пробуйте увеличивать `` пока push не сработает.
```sh
(main)$ git push -u origin HEAD~:refs/head/main --force
```
После того как только команда push сработала в первый раз, постепенно уменьшайте ``, пока не сработает обычная `git push`.
### Мне нужно изменить содержимое коммита, который не является последним
Пусть Вы сделали несколько (например, три) коммитов и после этого поняли, что упустили что-то, что по смыслу относится к первому из этих трёх коммитов. Вы могли бы сделать новый коммит, содержащий эти изменения, и тогда у Вас была бы чистая кодовая база, но Ваши коммиты не были бы атомарными (т.е. связанные изменения не содержались бы в едином коммите). В такой ситуации Вы бы хотели изменить коммит, к которому относятся эти изменения, а следующие коммиты оставить как есть. В таком случае Вас может спасти `git rebase`.
Рассмотрим ситуацию, когда Вы хотите изменить третий с конца коммит.
```sh
(your-branch)$ git rebase -i HEAD~4
```
запустит интерактивный режим rebase, что позволит Вам отредактировать любые из Ваших последних трёх коммитов. Появится текстовый редактор с подобным содержанием:
```sh
pick 9e1d264 The third last commit
pick 4b6e19a The second to last commit
pick f4037ec The last commit
```
которое Вам нужно изменить так:
```sh
edit 9e1d264 The third last commit
pick 4b6e19a The second to last commit
pick f4037ec The last commit
```
Это говорит rebase, что Вы хотите изменить третий с конца коммит, а другие два оставить как есть. Теперь сохраните и закройте редактор. После этого Git начнет процедуру rebase. Он остановится на коммите, который Вы пометили для изменений, давая Вам возможность изменить этот коммит. Теперь Вы можете доделать то, что забыли первоначально. Отредактируйте и подготовьте изменения. После этого запустите
```sh
(your-branch)$ git commit --amend
```
это заставит Git пересоздать коммит, но оставить сообщение коммита прежним. После этого тяжелая часть работы завершена.
```sh
(your-branch)$ git rebase --continue
```
сделает оставшуюся работу за Вас.
## Подготовка изменений (staging)
### Мне нужно добавить подготовленные изменения в предыдущий коммит
```sh
(my-branch*)$ git commit --amend
```
Если Вы не хотите менять сообщение коммита, скажите git использовать прежнее сообщение:
```sh
(my-branch*)$ git commit --amend -C HEAD
```
### Я хочу подготовить только часть файла, а не весь файл целиком
Обычно, если хотите подготовить часть файл, Вы запускаете:
```sh
$ git add --patch filename.x
```
`-p` - сокращение для `--patch`. Это откроет интерактивный режим. Вы сможете разбить коммит с помощью опции `s`, однако, если файл новый, то у Вас не будет такой возможности. При добавлении нового файла делайте так:
```sh
$ git add -N filename.x
```
Затем используйте опцию `e` для ручного выбора строк. Запустив `git diff --cached` или
`git diff --staged`, Вы увидите какие строки вы подготовили по-сравнению с тем, что сохранено в рабочей копии.
### Я хочу добавить изменения одного файла в два разных коммита
`git add` добавляет в коммит весь файл целиком. `git add -p` позволяет интерактивно выбрать изменения, которые Вы хотите добавить.
### Я подготовил слишком много правок и теперь хочу разделить их на несколько отдельных коммитов
`git reset -p` откроет интерактивный диалог сброса. Это похоже на `git add -p`, за исключением того, что выбор "yes" уберёт правку из готовящегося коммита.
### Я хочу подготовить свои неподготовленные правки и убрать из подготовки то, что уже подготовлено
Это сложно. Лучшее, что я смог придумать это отложить (stash) неподготовленные изменения. Затем сделать сброс. После этого вернуть отложенные изменения и добавить их.
```sh
$ git stash -k
$ git reset --hard
$ git stash pop
$ git add -A
```
## Неподготовленные правки
### Я хочу переместить мои неподготовленные правки в новую ветку
```sh
$ git checkout -b my-branch
```
### Я хочу переместить неподготовленные правки в другую существующую ветку
```sh
$ git stash
$ git checkout my-branch
$ git stash pop
```
### Я хочу отменить мои локальные несохраненные изменения (подготовленные и неподготовленные)
Если Вы хотите отменить все подготовленные и неподготовленные изменения, то можете сделать так:
```sh
(my-branch)$ git reset --hard
# or
(main)$ git checkout -f
```
Это уберет из индекса все подготовленные изменения:
```sh
$ git reset
```
Это удалит все локальные изменения, которые не были сохранены или добавлены в индекс (нужно запускать из корня репозитория):
```sh
$ git checkout .
```
Вы также можете отменить несохраненные изменения в определенном файле или папке:
```sh
$ git checkout [some_dir|file.txt]
```
Еще один способ отменить все несохраненные изменения (длиннее, зато работает в любой папке):
```sh
$ git reset --hard HEAD
```
Это удалит все локальные неотслеживаемые файлы, так что останутся только отслеживаемые:
```sh
$ git clean -fd
```
`-x` удалит также и игнорируемые файлы.
### Я хочу отменить некоторые неподготовленные изменения
Когда Вы хотите избавиться от некоторых, но не всех изменений в Вашей рабочей копии.
Сделайте checkout ненужных изменений, оставив нужные.
```sh
$ git checkout -p
# Отвечайте `y` для всех фрагментов, которые Вы хотите выбросить
```
Другим подходом является использование stash. Отложите все хорошие изменения, сбросьте рабочую копию и верните отложенные хорошие изменения.
```sh
$ git stash -p
# Выберите фрагменты, которые Вы хотите сохранить
$ git reset --hard
$ git stash pop
```
В качестве альтернативы, отложите ненужные изменения, а затем выбросьте их.
```sh
$ git stash -p
# Выберите фрагменты, которые Вы не хотите сохранять
$ git stash drop
```
### Я хочу отбросить неподготовленные изменения в некоторых файлах
Когда Вы хотите убрать изменения какого-то файла в Вашей рабочей копии.
```sh
$ git checkout myFile
```
Чтобы убрать изменения в нескольких файлах, перечислите их имена.
```sh
$ git checkout myFirstFile mySecondFile
```
### Я хочу убрать все неподготовленные локальные изменения
Когда Вы хотите убрать все неподготовленные локальные изменения
```sh
$ git checkout .
```
### Я хочу удалить все неотслеживаемые файлы
Когда Вы хотите удалить все неотслеживаемые файлы
```sh
$ git clean -f
```
### Я хочу убрать заданный файл из подготовленного
Иногда один или несколько неотслеживаемых файлов ненароком оказываются в подготовленном. Чтобы убрать их из подготовленного:
```sh
$ git reset --
```
Это уберёт файл из подготовленного и он снова будет выглядеть как неотслеживаемый.
## Ветки
### Я хочу получить список всех веток
Список локальных веток
```sh
$ git branch
```
Список удаленных веток
```sh
$ git branch -r
```
Список всех веток (локальных и удаленных)
```sh
$ git branch -a
```
### Создать ветку на определенном коммите
```sh
$ git checkout -b
```
### Я стянул изменения (pull) из неправильной ветки или в неправильную ветку
Это очередная возможность воспользоваться `git reflog`, чтобы посмотреть куда указывала ваша HEAD перед неправильным pull.
```sh
(main)$ git reflog
ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward
c5bc55a HEAD@{1}: checkout: checkout message goes here
```
Просто сбросьте ветку обратно на требуемый коммит:
```sh
$ git reset --hard c5bc55a
```
Готово.
### Я хочу отменить локальные коммиты, чтобы моя ветка стала такой же как на сервере
Подтвердите, что не хотите отправлять изменения на сервер.
`git status` покажет на сколько коммитов Вы опережаете источник:
```sh
(my-branch)$ git status
# On branch my-branch
# Your branch is ahead of 'origin/my-branch' by 2 commits.
# (use "git push" to publish your local commits)
#
```
Один из способов сбросить до источника (чтобы иметь то же, что и в удаленном репозитории):
```sh
(my-branch)$ git reset --hard origin/my-branch
```
### Я сохранил коммит в ветку main вместо новой ветки
Создайте новую ветку, оставаясь на main:
```sh
(main)$ git branch my-branch
```
Сбросьте ветку main к предыдущему коммиту:
```sh
(main)$ git reset --hard HEAD^
```
`HEAD^` - это сокращение для `HEAD^1`. Это подставляет первого предка `HEAD`, подобно этому `HEAD^2` подставляет второго предка коммита (слияния могут иметь 2 предков).
Заметьте, что `HEAD^2` это **не** то же самое, что `HEAD~2` (для подробностей см. [эту ссылку](http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde)).
Если не хотите использовать `HEAD^`, то найдите хэш коммита, на который Вы хотите установить ветку (`git log` может помочь в этом). Затем сделайте сброс к этому хэшу. С помощью `git push` удостоверьтесь, что эти изменения отражены в удаленном репозитории.
К примеру, ветка main обязана находится на коммите с хэшем `a13b85e`:
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
Перейти на новую ветку для продолжения работы:
```sh
(main)$ git checkout my-branch
```
### Я хочу сохранить файл целиком из другого ref-ish
Скажем, у Вас рабочий spike (см. заметку) на сотни изменений. Всё работает. Теперь Вы сохраняете эту работу в другую ветку:
```sh
(solution)$ git add -A && git commit -m "Добавлены все изменения из этого рывка в один большой коммит."
```
Когда Вы хотите поместить его в ветку (например `feature` или `develop`), Вы хотите сохранять по целым файлам. А также Вы хотите разбить большой коммит на несколько небольших.
Скажем, Вы имеете:
- ветку `solution` с решением к Вашему spike. На один коммит впереди `develop`.
- ветку `develop`, куда Вы хотите добавить Ваши изменения.
Вы можете выполнить это, перенеся содержимое файла в Вашу ветку:
```sh
(develop)$ git checkout solution -- file1.txt
```
Это скопирует содержимое данного файла из ветки `solution` в ветку `develop`:
```sh
# On branch develop
# Your branch is up-to-date with 'origin/develop'.
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: file1.txt
```
Теперь сделайте коммит как обычно.
Заметка: Spike-решения делаются для анализа или решения проблемы. Эти решения используют, чтобы оценить проблему, и отбрасывают сразу же, как только все получают ясное представление о проблеме. ~ [Wikipedia](https://en.wikipedia.org/wiki/Extreme_programming_practices).
### Я сделал несколько коммитов в одной ветке, а нужно было сохранять их в разных ветках
Скажем, Вы в ветке main. Запустив `git log`, Вы увидите, что сделали два коммита:
```sh
(main)$ git log
commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee
Date: Tue Jul 22 15:39:27 2014 -0400
Bug #21 - Added CSRF protection
commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee
Date: Tue Jul 22 15:39:12 2014 -0400
Bug #14 - Fixed spacing on title
commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose
Date: Tue Jul 21 01:12:48 2014 -0400
First commit
```
Обратим внимание на ссылки на каждый баг (`e3851e8` для #21, `5ea5173` для #14).
Во-первых, сбросим ветку main на правильный коммит (`a13b85e`):
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
Теперь, мы может создать новую ветку для бага #21:
```sh
(main)$ git checkout -b 21
(21)$
```
Теперь сделаем *cherry-pick* коммита для бага #21 на верх нашей ветки. Это значит, что мы помещаем этот и только этот коммит напрямую на вершину ветки, какой бы она ни была.
```sh
(21)$ git cherry-pick e3851e8
```
На этом этапе есть вероятность конфликтов. О том как разрешить конфликты см. в главе [**Здесь были конфликты**](#merge-conflict) в [разделе интерактивное перебазирование выше](#interactive-rebase).
Теперь давайте создадим новую ветку для бага #14, которая также основана на main
```sh
(21)$ git checkout main
(main)$ git checkout -b 14
(14)$
```
И наконец, сделаем cherry-pick коммита для бага #14:
```sh
(14)$ git cherry-pick 5ea5173
```
### Я хочу удалить локальные ветки, которые были удалены в upstream
Как только Вы слили пулл-реквест на GitHub, Вам предлагают удалить слитую ветку из Вашего форка. Если Вы не планируете продолжать работу в этой ветке, то для поддержания рабочей копии в чистоте Вы можете удалить локальные копии ненужных веток, чтобы не путаться в них.
```sh
$ git fetch -p upstream
```
где `upstream` - удаленная ветка, из которой Вы хотите получить изменения.
### Я нечаянно удалил мою ветку
Если Вы регулярно отправляете изменения в удаленное хранилище, большую часть времени Вы в безопасности. Но в один прекрасный момент Вы всё же можете случайно удалить Ваши ветки. Скажем, мы создали ветку и создали новый файл:
```sh
(main)$ git checkout -b my-branch
(my-branch)$ git branch
(my-branch)$ touch foo.txt
(my-branch)$ ls
README.md foo.txt
```
Давайте добавим его и сохраним.
```sh
(my-branch)$ git add .
(my-branch)$ git commit -m 'foo.txt added'
(my-branch)$ foo.txt added
1 files changed, 1 insertions(+)
create mode 100644 foo.txt
(my-branch)$ git log
commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
Author: siemiatj
Date: Wed Jul 30 00:34:10 2014 +0200
foo.txt added
commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson
Date: Tue Jul 29 13:14:46 2014 -0400
Fixes #6: Force pushing after amending commits
```
Теперь мы переключаемся обратно на main и 'нечаянно' удаляем нашу ветку.
```sh
(my-branch)$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
(main)$ git branch -D my-branch
Deleted branch my-branch (was 4e3cd85).
(main)$ echo О, нет, моя ветка удалена!
О, нет, моя ветка удалена!
```
На этом этапе Вы должны быть знакомы с 'reflog' (расширенный журнал). Он хранит историю всех действий в репозитории.
```
(main)$ git reflog
69204cd HEAD@{0}: checkout: moving from my-branch to main
4e3cd85 HEAD@{1}: commit: foo.txt added
69204cd HEAD@{2}: checkout: moving from main to my-branch
```
Как мы можем видеть, у нас есть хэш коммита из удаленной ветки. Посмотрим, можем ли мы восстановить удаленную ветку.
```sh
(main)$ git checkout -b my-branch-help
Switched to a new branch 'my-branch-help'
(my-branch-help)$ git reset --hard 4e3cd85
HEAD is now at 4e3cd85 foo.txt added
(my-branch-help)$ ls
README.md foo.txt
```
Вуаля! Мы вернули наш удаленный файл обратно. `git reflog` также бывает полезен, когда перебазирование срабатывает не так, как Вы хотели.
### Я хочу удалить ветку
Чтобы удалить ветку на удаленном репозитории:
```sh
(main)$ git push origin --delete my-branch
```
Вы также можете сделать:
```sh
(main)$ git push origin :my-branch
```
Чтобы удалить локальную ветку:
```sh
(main)$ git branch -d my-branch
```
Чтобы удалить локальную ветку, которая *не была* слита с отслеживаемой веткой (заданной с помощью `--track` или `--set-upstream`) или с HEAD:
```sh
(main)$ git branch -D my-branch
```
### Я хочу удалить несколько веток
Скажем, Вы хотите удалить все ветки, начинающиеся с `fix/`:
```sh
(main)$ git branch | grep 'fix/' | xargs git branch -d
```
### Я хочу переименовать ветку
Чтобы переименовать текущую (локальную) ветку:
```sh
(main)$ git branch -m new-name
```
Чтобы переименовать другую (локальную) ветку:
```sh
(main)$ git branch -m old-name new-name
```
### Я хочу перейти на удаленную ветку, над которой работает кто-то еще
Во-первых, получим все ветки из удаленного репозитория:
```sh
(main)$ git fetch --all
```
Скажем, Вы хотите перейти на `daves` из удаленного репозитория.
```sh
(main)$ git checkout --track origin/daves
Branch daves set up to track remote branch daves from origin.
Switched to a new branch 'daves'
```
(`--track` - это сокращение для `git checkout -b [branch] [remotename]/[branch]`)
Это создаст Вам локальную копию ветки `daves` и после `push` обновления также появятся в удаленном репозитории.
### Я хочу создать новую удаленную ветку из текущей локальной
```sh
$ git push HEAD
```
Если Вы хотите, чтобы текущая локальная ветка отслеживала соответствующую удаленную (upstream) ветку, тогда выполните следующее:
```sh
$ git push -u HEAD
```
В режиме `upstream` или в режиме `simple` (по-умолчанию в Git 2.0) параметра `push.default`, следующая команда отправит текущую ветку в удаленную ветку, которая была ранее зарегистрирована с помощью -u :
```sh
$ git push
```
Поведение других режимов `git push` описано в документации на push.default.
### Я хочу настроить локальную ветку на отслеживание удаленной (upstream) ветки
Вы можете настроить текущую локальную ветку на отслеживание удаленной (upstream) ветки используя:
```sh
$ git branch --set-upstream-to [remotename]/[branch]
# или для краткости:
$ git branch -u [remotename]/[branch]
```
Для настройки отслеживаемой удаленной ветки на другую локальную ветку:
```sh
$ git branch -u [remotename]/[branch] [local-branch]
```
### Я хочу настроить HEAD на отслеживание основной удаленной ветки
При просмотре удаленных веток можно увидеть какую удаленную ветку отслеживает HEAD. Может оказаться, что это не та ветка что нужно.
```sh
$ git branch -r
origin/HEAD -> origin/gh-pages
origin/main
```
Чтобы `origin/HEAD` отслеживала `origin/main`, выполните команду:
```sh
$ git remote set-head origin --auto
origin/HEAD set to main
```
### Я сделал изменения в неправильной ветке
Вы сделали несохраненные изменения, а потом поняли, что находитесь не в той ветке. Отложите эти изменения, а затем примените их к нужной ветке:
```sh
(wrong_branch)$ git stash
(wrong_branch)$ git checkout
(correct_branch)$ git stash apply
```
## Перебазирование (rebase) и слияние (merge)
### Я хочу отменить перебазирование/слияние
Вы можете слить/перебазировать Вашу ветку с неправильной веткой. А также бывают случаи, когда Вы не можете предугадать успешно ли завершится процесс перебазирования/слияния. Git сохраняет исходный указатель HEAD в переменную ORIG_HEAD перед тем как приступить к опасным операциям, так что вернуть ветку на состояние до перебазирования/слияния просто.
```sh
(my-branch)$ git reset --hard ORIG_HEAD
```
### Я сделал перебазирование, но я не хочу делать принудительный push
К сожалению, вы должны сделать принудительный push, если хотите, чтобы изменения были отражены на удаленной ветке. Это потому что у вас изменена история. Удаленная ветка не примет изменения, если не сделать принудительный push. Это одна из основных причин, по которым большинство людей основывает свой рабочий процесс на слиянии вместо перебазирования - большие команды могут столкнуться с проблемами, если разработчики будут делать принудительный `push`. Используйте это с осторожностью. Безопасный способ использовать перебазирование - это не отражать Ваши изменения напрямую на удаленную ветку, а вместо этого делать следующее:
```sh
(main)$ git checkout my-branch
(my-branch)$ git rebase -i main
(my-branch)$ git checkout main
(main)$ git merge --ff-only my-branch
```
Чтобы узнать больше, см. [эту SO ветку](https://stackoverflow.com/questions/11058312/how-can-i-use-git-rebase-without-requiring-a-forced-push).
### Я хочу объединить коммиты
Предположим, что Вы работаете в ветке, которая стала или станет пулл-реквестом в `main`. В простейшем случае когда всё, что Вы хотите сделать - это объединить *все* коммиты в один единственный коммит и Вам не важны временные метки, Вы можете сделать сброс и заново сделать коммит. Убедитесь, что ветка main обновлена и Ваши изменения сохранены, затем:
```sh
(my-branch)$ git reset --soft main
(my-branch)$ git commit -am "New awesome feature"
```
Если Вы хотите больше контроля, а также сохранить метки времени, Вам нужно сделать кое-что, называемое интерактивным перебазированием:
```sh
(my-branch)$ git rebase -i main
```
Если Вы не работаете с другой веткой, можете делать перебазирование относительно Вашей `HEAD`. Например, если Вы хотите объединить последние два коммита, Вам нужно делать перебазирование относительно `HEAD~2`. Для последних 3 - `HEAD~3` и т.д.
```sh
(main)$ git rebase -i HEAD~2
```
После того, как запустите интерактивное перебазирование, Вы увидите нечто подобное в Вашем текстовом редакторе:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
pick b729ad5 fixup
pick e3851e8 another fix
# Rebase 8074d12..b729ad5 onto 8074d12
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```
Все строки, начинающиеся с `#` являются комментариями и не оказывают влияния на перебазирование.
Теперь замените команды `pick` на команды, перечисленные ниже. Также Вы можете удалить коммиты, удалив соответствующие строки.
Например, если Вы хотите **оставить только старейший коммит и объединить все последующие коммиты во второй коммит**, Вам нужно рядом с каждым коммитом кроме первого и второго вместо `pick` написать `f`:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
f b729ad5 fixup
f e3851e8 another fix
```
Если Вы хотите объединить эти коммиты **и переименовать коммит**, Вам нужно дополнительно добавить `r` рядом со вторым коммитом или просто используйте `s` вместо `f`:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
s b729ad5 fixup
s e3851e8 another fix
```
Теперь Вы можете переименовать коммит в следующем запросе, который появится.
```vim
Newer, awesomer features
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 8074d12
# You are currently editing a commit while rebasing branch 'main' on '8074d12'.
#
# Changes to be committed:
# modified: README.md
#
```
Если всё успешно, Вы увидите что-то вроде:
```sh
(main)$ Successfully rebased and updated refs/heads/main.
```
#### Безопасная стратегия слияния
`--no-commit` производит слияние, но не делает коммит результата, давая пользователю возможность проверить и доработать результат слияния перед коммитом. `no-ff` сохраняет доказательства того, что когда-то существовала ветка feature, сохраняя цельность истории проекта.
```sh
(main)$ git merge --no-ff --no-commit my-branch
```
#### Мне нужно слить ветку в единственный коммит
```sh
(main)$ git merge --squash my-branch
```
#### Я хочу объединить только неопубликованные коммиты
Иногда у Вас бывает несколько временных коммитов, которые Вы хотите объединить перед публикацией в upstream. Вы не хотите ненароком объединить свои коммиты с уже опубликованными, потому что кто-то уже мог работать с ними.
```sh
(main)$ git rebase -i @{u}
```
Это выполнит интерактивное перебазирование со списком еще не опубликованных коммитов и Вы сможете безопасно упорядочить/исправить/объединить коммиты из списка.
#### Мне нужно прервать слияние
Иногда слияние может создавать проблемы в некоторых файлах. В таких случаях мы можем воспользоваться опцией `abort` для прерывания текущего процесса разрешения конфликтов и попробовать вернуться к состоянию перед слиянием.
```sh
(my-branch)$ git merge --abort
```
Эта команда доступна начиная с версии Git >= 1.7.4
### Мне нужно обновить родительский коммит моей ветки
Скажем, у меня есть ветка main, ветка feature-1, выросшая из main, и ветка feature-2, выросшая из feature-1. Если я сделаю коммит в feature-1, тогда родительский коммит ветки feature-2 будет не тем, что нужно (это должна быть верхушка feature-1, поскольку мы ответвились от неё). Мы можем исправить это с помощью `git rebase --onto`.
```sh
(feature-2)$ git rebase --onto feature-1 <первый коммит из feature-2, который Вы не хотите переносить> feature-2
```
Это помогает в тех случаях, когда у Вас одна функция основывается на другой функции, которая еще не слита, и все исправления feature-1 должны быть отражены в feature-2.
### Проверить, что все коммиты ветви были слиты
Для проверки того, что все коммиты ветки слиты в другую ветку, Вам нужно сравнить вершины (или любые коммиты) этих ветвей:
```sh
(main)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
```
Это расскажет Вам обо всех коммитах, которые есть в одной, но отсутствуют в другой ветке и выдаст список всех различий ветвей. Или можете сделать так:
```sh
(main)$ git log main ^feature/120-on-scroll --no-merges
```
### Возможные проблемы интерактивного перебазирования
#### Экран перебазирования говорит 'noop'
Если Вы видите это:
```
noop
```
Это значит, что Вы пытаетесь сделать перебазирование в ветку, которая уже имеет идентичный коммит или она *впереди* текущей ветки. Вы может попробовать:
- удостовериться что Ваша ветка main находится там, где она должна быть
- вместо этого перебазировать `HEAD~2` или что-то более раннее
#### Здесь были конфликты
Если Вам не удается успешно завершить перебазирование, то, возможно, Вам придется разрешать конфликты.
Для начала запустите `git status`, чтобы увидеть какие файлы содержат конфликты:
```sh
(my-branch)$ git status
On branch my-branch
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
both modified: README.md
```
В данном примере `README.md` содержит конфликты. Откройте файл и взгляните на следующее:
```vim
<<<<<<< HEAD
some code
=========
some code
>>>>>>> new-commit
```
Вам нужно выбрать между кодом, который был добавлен в Вашем новом коммите (в данном примере, это все от средней линии до `new-commit`) и Вашей `HEAD`.
Если Вы хотите сохранить версию из какой-то одной ветки, то используйте `--ours` или `--theirs`:
```sh
(main*)$ git checkout --ours README.md
```
- Во время *слияния* используйте `--ours` для сохранения изменений из локальной ветки или `--theirs` для сохранения изменений из другой ветки.
- Во время *перебазирования* используйте `--theirs` для сохранения изменений из локальной ветки или `--ours` для сохранения изменений из другой ветки. Для объяснения такого обмена см. [эту заметку в документации Git](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---merge).
Если слияние более сложное, можете воспользоваться визуальным редактором различий:
```sh
(main*)$ git mergetool -t opendiff
```
После разрешения всех конфликтов и тестирования кода подготовьте файлы, которые Вы изменили `git add`, а затем продолжите перебазирование с помощью `git rebase --continue`
```sh
(my-branch)$ git add README.md
(my-branch)$ git rebase --continue
```
Если после разрешения всех конфликтов, Вы получили точно такое же дерево, какое было перед коммитом, то вместо этого Вам нужно сделать `git rebase --skip`.
В любой момент Вы можете остановить перебазирование и вернуть ветку в начальное состояние, выполнив:
```sh
(my-branch)$ git rebase --abort
```
## Отложенные изменения (stash)
### Отложить все правки
Чтобы отложить все правки в рабочем каталоге
```sh
$ git stash
```
Если Вы хотите отложить заодно и неотслеживаемые файлы, добавьте опцию `-u`.
```sh
$ git stash -u
```
### Отложить заданные файлы
Отложить только один файл из рабочей папки
```sh
$ git stash push working-directory-path/filename.ext
```
Отложить несколько файлов из рабочей папки
```sh
$ git stash push working-directory-path/filename1.ext working-directory-path/filename2.ext
```
### Отложить с сообщением
```sh
$ git stash save
```
### Применить заданный stash из списка
Во-первых, проверьте список отложенных изменений, используя
```sh
$ git stash list
```
Затем примените заданный stash из списка, используя
```sh
$ git stash apply "stash@{n}"
```
Здесь 'n' показывает позицию stash-а в стеке. Верхний stash имеет позицию 0.
## Поиск
### Я хочу найти строку в коммитах
Чтобы найти коммиты с заданной строкой, используйте следующее:
```sh
$ git log -S "string to find"
```
Общие параметры:
- `--source` показывает ссылки, от которых можно добраться до каждого коммита.
- `--all` ищет во всех ветках.
- `--reverse` выводит коммиты в обратном порядке, это значит, что вверху будет первый коммит, в котором сделано это изменение.
### Я хочу искать по автору или сохранившему изменения (committer)
Найти все коммиты по автору или сохранившему изменения:
```sh
$ git log --author=
$ git log --committer=
```
Не забывайте, что автор и сохранивший изменения - это не всегда один и тот же человек. `--author` - это тот, кто написал код, а `--committer` - тот, кто сохранил код, написанный автором.
### Я хочу получить список коммитов, содержащих заданный файл
Чтобы найти все коммиты, содержащие заданный файл, Вы можете использовать:
```sh
$ git log --
```
Обычно Вы задаете точный путь, но можете использовать подстановочные знаки:
```sh
$ git log -- **/*.js
```
При использовании подстановочных знаков используйте `--name-status` для просмотра списка файлов, сохраненных в каждом коммите:
```sh
$ git log --name-status -- **/*.js
```
### Я хочу посмотреть историю коммитов для отдельной функции
Для отслеживания эволюции отдельной функции Вы можете использовать:
```sh
$ git log -L :FunctionName:FilePath
```
Заметьте, что Вы можете добавить и другие опции команды `git log`, вроде [диапазона ревизий](https://git-scm.com/docs/gitrevisions) и [выборки коммитов](https://git-scm.com/docs/git-log/#_commit_limiting).
### Найти метки для заданного коммита
Чтобы найти все метки для заданного коммита:
```sh
$ git tag --contains
```
## Субмодули
### Клонировать все субмодули
```sh
$ git clone --recursive git://github.com/foo/bar.git
```
Если уже клонированы:
```sh
$ git submodule update --init --recursive
```
### Удалить субмодуль
Создание субмодуля довольно прямолинейно, чего не скажешь об удалении. Команды, которые Вам нужны:
```sh
$ git submodule deinit submodulename
$ git rm submodulename
$ git rm --cached submodulename
$ rm -rf .git/modules/submodulename
```
## Разное
### Восстановить удаленный файл
Сначала нужно найти последний коммит, где файл еще существует:
```sh
$ git rev-list -n 1 HEAD -- filename
```
Затем взять версию файла из коммита непосредственно перед удалением:
```
git checkout deletingcommitid^ -- filename
```
### Удалить метку
```sh
$ git tag -d
$ git push :refs/tags/
```
### Восстановить удаленную метку
Если хотите восстановить метку, которая была удалена, Вы можете сделать следующее: во-первых, Вам нужно найти недостижимую метку:
```sh
$ git fsck --unreachable | grep tag
```
Запомните для себя хэш метки. Затем восстановите удаленную метку, используя команду [`git update-ref`](https://git-scm.com/docs/git-update-ref):
```sh
$ git update-ref refs/tags/
```
Ваша метка была восстановлена.
### Удаленный патч
Если кто-то прислал Вам пулл-реквест на GitHub, но потом удалил свой форк, то вы не сможете клонировать его репозиторий или использовать `git am`, поскольку [.diff, .patch](https://github.com/blog/967-github-secrets) URL'ы становятся недоступными. Но Вы можете сделать `checkout` самого пулл-реквеста используя [специальные GitHub's refs](https://gist.github.com/piscisaureus/3342247). Для получения содержимого PR#1 в новую ветку с названием pr_1:
```sh
$ git fetch origin refs/pull/1/head:pr_1
From github.com:foo/bar
* [new ref] refs/pull/1/head -> pr_1
```
### Экспорт репозитория в Zip-файл
```sh
$ git archive --format zip --output /full/path/to/zipfile.zip main
```
### Как опубликовать ветку и метку, если они имеют одинаковые имена
Если в удаленном репозитории есть метка с таким же именем, как и ветка , то после выполнения команды `$ git push ` Вы получите следующую ошибку:
```sh
$ git push origin
error: dst refspec same matches more than one.
error: failed to push some refs to ''
```
Исправить это можно, указав Вы хотите опубликовать именно ветку.
```sh
$ git push origin refs/heads/
```
Если Вы хотите опубликовать метку с таким же названием, как у ветки, используйте сходную команду.
```sh
$ git push origin refs/tags/
```
## Отслеживание файлов
### Я хочу изменить регистр в имени файла, не меняя содержимое файла
```sh
(main)$ git mv --force myfile MyFile
```
### Я хочу переписать локальные файлы при выполнении git pull
```sh
(main)$ git fetch --all
(main)$ git reset --hard origin/main
```
### Я хочу удалить файл из git, но оставить сам файл
```sh
(main)$ git rm --cached log.txt
```
### Я хочу откатить файл до заданной ревизии
Полагая, что хэш желаемого коммита c5f567:
```sh
(main)$ git checkout c5f567 -- file1/to/restore file2/to/restore
```
Если вы хотите откатить файл к состоянию на 1 коммит раньше, чем c5f567, задайте хэш коммита как c5f567~1:
```sh
(main)$ git checkout c5f567~1 -- file1/to/restore file2/to/restore
```
### Я хочу получить список изменений в заданном файле из разных коммитов или веток
Полагая, что Вы хотите сравнить последний коммит с файлом из коммита c5f567:
```sh
$ git diff HEAD:path_to_file/file c5f567:path_to_file/file
# или
$ git diff HEAD c5f567 -- path_to_file/file
```
Аналогично для веток:
```sh
$ git diff main:path_to_file/file staging:path_to_file/file
# или
$ git diff main staging -- path_to_file/file
```
### Я хочу, чтобы Git игнорировал изменения в определенном файле
Это бывает нужно для шаблонов конфигураций или других файлов, предназначенных для добавления учетных записей, которые не нужно коммитить.
```sh
$ git update-index --assume-unchanged file-to-ignore
```
Обратите внимание, что это *не* удаляет файл из контроля версий - он всего лишь игнорируется локально. Чтобы отменить это и сказать Git снова отслеживать изменения, выполните обратную команду:
```sh
$ git update-index --no-assume-unchanged file-to-stop-ignoring
```
## Отладка с помощью Git
Команда [git-bisect](https://git-scm.com/docs/git-bisect) с помощью двоичного поиска находит в истории Git коммит, в котором впервые появилась ошибка.
Пусть вы находитесь на ветке `main` и хотите найти коммит, которых поломал некую функцию. Вы начинаете процедуру поиска:
```sh
$ git bisect start
```
Затем вы должны указать коммит, где функция поломана (bad), коммит, где функция работает правильно (good). Предположим, что *текущая* версия поломана, а `v1.1.1` - работоспособная:
```sh
$ git bisect bad
$ git bisect good v1.1.1
```
После этого `git-bisect` выберет коммит из середины заданного вами диапазона, перейдет на него и спросит у вас: хороший он или плохой. Вы увидите что-то вроде:
```sh
$ Bisecting: 5 revision left to test after this (roughly 5 step)
$ [c44abbbee29cb93d8499283101fe7c8d9d97f0fe] Commit message
$ (c44abbb)$
```
Вы проверите хороший ли коммит. Если хороший:
```sh
$ (c44abbb)$ git bisect good
```
и `git-bisect` выберет другой коммит. Этот процесс (выбора `good` или `bad`) будет повторяться до тех пор, пока диапазон поиска не сузится до единственного коммита, после чего команда выведет описание **первого** плохого коммита.
## Конфигурация
### Я хочу добавить псевдонимы для некоторых команд Git
В OS X и Linux Ваш файл конфигурации Git хранится в `~/.gitconfig`. В качестве примера я добавил некоторые псевдонимы, которые сам использую для краткости (а также некоторые из моих типичных опечаток), в раздел `[alias]` как показано ниже:
```vim
[alias]
a = add
amend = commit --amend
c = commit
ca = commit --amend
ci = commit -a
co = checkout
d = diff
dc = diff --changed
ds = diff --staged
extend = commit --amend -C HEAD
f = fetch
loll = log --graph --decorate --pretty=oneline --abbrev-commit
m = merge
one = log --pretty=oneline
outstanding = rebase -i @{u}
reword = commit --amend --only
s = status
unpushed = log @{u}
wc = whatchanged
wip = rebase -i @{u}
zap = fetch -p
day = log --reverse --no-merges --branches=* --date=local --since=midnight --author=\"$(git config --get user.name)\"
delete-merged-branches = "!f() { git checkout --quiet main && git branch --merged | grep --invert-match '\\*' | xargs -n 1 git branch --delete; git checkout --quiet @{-1}; }; f"
```
### Я хочу добавить в свой репозиторий пустую папку
Вы не можете этого сделать! Git просто не поддерживает этого, но есть уловка. Вы можете создать файл .gitignore в папке со следующим содержанием:
```
# Игнорировать всё в этой папке
*
# Кроме этого файла
!.gitignore
```
Другой общеиспользуемый способ - это создать в папке пустой файл с названием .gitkeep.
```sh
$ mkdir mydir
$ touch mydir/.gitkeep
```
Вы можете назвать его просто .keep , в этом случае вторая строка выше будет `touch mydir/.keep`
### Я хочу сохранить имя пользователя и пароль для репозитория
У Вас может быть репозиторий, требующий авторизации. В этом случае вы можете сохранить на время имя пользователя и пароль, и Вам не потребуется вводить их при каждом вызове push или pull. Помощник по учетным записям сделает это за Вас.
```sh
$ git config --global credential.helper cache
# Включает кэширование памяти учетных записей
```
```sh
$ git config --global credential.helper 'cache --timeout=3600'
# Задает таймаут для кэша 1 час (задается в секундах)
```
Найти помощника по учетным записям:
```sh
$ git help -a | grep credential
# Покажет доступных помощников по учетным записям
```
Специфическое кэширование учетных записей для некоторых ОС:
```sh
$ git config --global credential.helper osxkeychain
# Для OSX
```
```sh
$ git config --global credential.helper manager
# Git for Windows 2.7.3+
```
```sh
$ git config --global credential.helper gnome-keyring
# Ubuntu и другие дистрибутивы, основанные на GNOME
```
Вероятно можно найти и других помощников по учетным записям для других дистрибутивов и ОС.
### Я хочу, чтобы Git игнорировал изменения разрешений и прав файлов
```sh
$ git config core.fileMode false
```
Если Вы хотите задать это поведение по-умолчанию для всех авторизованных пользователей, тогда используйте:
```sh
$ git config --global core.fileMode false
```
### Я хочу задать данные пользователя в глобальных настройках
Чтобы настроить информацию о пользователе, используемую во всех локальных репозиториях, в частности имя, отображаемое в истории изменений:
```sh
$ git config --global user.name “[firstname lastname]”
```
Чтобы настроить адрес электронной почты, который будет связан с каждой записью в историю:
```sh
git config --global user.email “[valid-email]”
```
## Я не представляю что я сделал неправильно
Итак, Вы в затруднении - Вы сбросили что-то или Вы слили неправильную ветку, или Вы отправили изменения с принудительной перезаписью и теперь Вы не можете найти свои коммиты. Вы знаете, что в какой-то момент было всё в порядке и Вы хотите вернуться к этому состоянию.
Как раз для этого и нужен `git reflog`. `reflog` отслеживает все изменения вершины ветки, даже если на эту вершину не указывает ни единая ветвь или метка. В принципе, всякий раз при изменении HEAD, в reflog добавляется новая запись. К сожалению, это работает только с локальными репозиториями и отслеживаются только движения (а не, например, изменения файла, которые не были никуда записаны).
```sh
(main)$ git reflog
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
```
reflog выше показывает переход с ветки main на ветку 2.2 и обратно. Затем происходит жесткий сброс на старый коммит. Самое последнее действие находится вверху с надписью `HEAD@{0}`.
Если Вы случайно переместитесь назад, то reflog будет содержать коммит (0254ea7), на который указывала ветка main до того, как Вы случайно выбросили 2 коммита.
```sh
$ git reset --hard 0254ea7
```
С помощью `git reset` можно вернуть ветку main обратно на коммит, на котором она была прежде. Это обеспечивает безопасность при случайном изменении истории.
(взято из [Источник](https://www.atlassian.com/git/tutorials/rewriting-history/git-reflog)).
## Сокращения для команд Git
### Git Bash
Как только Вы освоитесь с командами, описанными выше, возможно, Вам захочется написать сокращения для Git Bash. Это позволит Вас работать гораздо быстрее, выполняя сложные задачи с помощью весьма коротких команд.
```sh
alias sq=squash
function squash() {
git rebase -i HEAD~$1
}
```
Скопируйте эти команды в Ваш .bashrc или .bash_profile.
### PowerShell в Windows
Если Вы пользуетесь PowerShell на Windows, то и здесь Вы можете настроить псевдонимы и функции. Добавьте эти команды в Ваш профиль, путь к которому указан в переменной `$profile`. Узнать больше о профилях можно на портале документации Microsoft [About Profiles](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles).
```powershell
Set-Alias sq Squash-Commits
function Squash-Commits {
git rebase -i HEAD~$1
}
```
# Другие ресурсы
## Книги
* [Learn Enough Git to Be Dangerous](https://www.learnenough.com/git-tutorial) - Книга Майкла Хартла, посвященная Git с самых основ
* [Pro Git](https://git-scm.com/book/ru/v2) - великолепная книга Скотта Чакона и Бена Страуба про Git
* [Git Internals](https://github.com/pluralsight/git-internals-pdf) - еще одна великолепная книга Скотта Чакона, посвященная Git
* [Nasa handbook](https://www.nasa.gov/sites/default/files/atoms/files/nasa_systems_engineering_handbook.pdf)
## Учебники
* [19 Git Tips For Everyday Use](https://www.alexkras.com/19-git-tips-for-everyday-use) - Список полезных однострочников Git
* [Atlassian's Git tutorial](https://www.atlassian.com/git/tutorials) Получите Git сразу с учебниками от начального до продвинутого уровня.
* [Изучаем ветвление в Git](https://learngitbranching.js.org/) Интерактивный веб-учебник по ветвлению/слиянию/перебазированию
* [Конструктивно о слиянии и перебазировании в Git](https://medium.com/@porteneuve/getting-solid-at-git-rebase-vs-merge-4fa1a48c53aa)
* [Git Commands and Best Practices Cheat Sheet](https://zeroturnaround.com/rebellabs/git-commands-and-best-practices-cheat-sheet) - Шпаргалка Git с подробными пояснениями
* [Git изнутри](https://codewords.recurse.com/issues/two/git-from-the-inside-out) - Учебник, погружающий во внутренности Git
* [git-workflow](https://github.com/asmeurer/git-workflow) - Руководство от [Aaron Meurer](https://github.com/asmeurer) по использованию Git в совместной разработке проектов с открытым исходным кодом
* [GitHub как рабочий процесс](https://hugogiraudel.com/2015/08/13/github-as-a-workflow/) - Интересный подход к использованию GitHub в качестве рабочего процесса, в частности с пустыми пулл-реквестами
* [Githug](https://github.com/Gazler/githug) - Игра для изучения более общих рабочих процессов Git
* [learnGitBranching](https://github.com/pcottle/learnGitBranching) - Изучение ветвлений git в игровой форме.
## Скрипты и инструменты
* [firstaidgit.io](http://firstaidgit.io/) Выборка наиболее частых вопросов и ответов по Git c поиском
* [git-extra-commands](https://github.com/unixorn/git-extra-commands) - сборник полезных дополнительных скриптов для Git
* [git-extras](https://github.com/tj/git-extras) - GIT utilities -- статистика репозитория, REPL, генерация журнала изменений, статистика по авторам изменений и многое другое
* [git-fire](https://github.com/qw3rtman/git-fire) - git-fire - это плагин для Git, который предупреждает при потенциально опасных действиях, таких как: добавление всех файлов из текущей папки, создание коммита и публикация измений в новую ветку (для предотвращения конфликтов при слиянии).
* [git-tips](https://github.com/git-tips/tips) - Краткие советы по Git
* [git-town](https://github.com/Originate/git-town) - Общая высокоуровневая поддержка рабочего процесса Git! http://www.git-town.com
## Графические клиенты
* [GitKraken](https://www.gitkraken.com/) - роскошный Git-клиент для Windows, Mac и Linux
* [git-cola](https://git-cola.github.io/) - еще один Git-клиент для Windows и OS X
* [GitUp](https://github.com/git-up/GitUp) - новый графический клиент, имеющий весьма своеобразные методы работы со сложностями Git
* [gitx-dev](https://rowanj.github.io/gitx/) - еще один графический Git-клиент для OS X
* [Sourcetree](https://www.sourcetreeapp.com/) - Простота и мощь в красивом и свободном графическом Git-клиенте. Для Windows и Mac.
* [Tower](https://www.git-tower.com/) - графический Git-клиент для OS X (платный)
* [tig](https://jonas.github.io/tig/) - консольный текстовый интерфейс для Git
* [Magit](https://magit.vc/) - интерфейс для Git, реализованный в виде модуля Emacs.
* [GitExtensions](https://github.com/gitextensions/gitextensions) - расширение оболочки, плагин для Visual Studio 2010-2015 и автономный инструмент для управления репозиториями Git.
* [Fork](https://git-fork.com/) - быстрый и дружелюбный Git-клиент для Mac (бета)
* [gmaster](https://gmaster.io/) - Git-клиент для Windows с трехсторонним слиянием, обнаружением рефакторинга, семантическим сравнением и слиянием (бета)
* [gitk](https://git-scm.com/docs/gitk) - Git-клиент под Linux для просмотра состояния репозитория.
* [SublimeMerge](https://www.sublimemerge.com/) - Шустрый, расширяемый клиент для трехстороннего слияния, мощного поиска и подсветки синтаксиса. В активной разработке.
================================================
FILE: README_vi.md
================================================
# Flight rules cho Git
🌍
*[English](README.md) ∙ [Español](README_es.md) ∙ [Русский](README_ru.md) ∙ [繁體中文](README_zh-TW.md) ∙ [简体中文](README_zh-CN.md) ∙ [한국어](README_kr.md) ∙ [Tiếng Việt](README_vi.md) ∙ [Français](README_fr.md) ∙ [日本語](README_ja.md)*
#### "Flight rules" là gì?
Là tài liệu hướng dẫn cho các phi hành gia vũ trụ (và tại đây, cho các lập trình viên sử dụng Git) về những việc cần làm khi có sai lầm xảy ra.
> *Flight Rules* là những kiến thức vất vả kiếm được trong các hướng dẫn sử dụng chỉ ra, từng bước, phải làm gì nếu X xảy ra và tại sao. Về cơ bản, chúng là các chuẩn quy trình thực hiện rất chi tiết cho từng kịch bản cụ thể . [...]
> NASA qua thời gian đã ghi lại những sai lầm, thảm hoạ và giải pháp của chúng tôi kể từ đầu những năm 1960, khi các đội mặt đất trong thời kỳ chương trình Mercury bắt đầu thu thập "các bài học kinh nghiệm" thành một bản yếu lược liệt kê hàng nghìn tình huống có vấn đề, từ lỗi động cơ đến các tay cầm bị bẻ cong đến trục trặc máy tính, và các giải pháp của họ.
— Chris Hadfield, *Sổ Tay Phi Hành Gia*.
#### Quy chuẩn cho tài liệu này
Để chuyền tải rõ ràng, tất cả các ví dụ trong tài liệu này sử dụng bash prompt được tuỳ chỉnh để chỉ ra nhánh hiện tại và có hay không thay đổi trong vùng chuyển tiếp (staged changes). Nhánh được đặt trong dấu ngoặc đơn và một ký tự `*` bên cạnh tên nhánh cho biết các thay đổi trong vùng chuyển tiếp.
Tất cả các lệnh (command) phải thi hành với phiên bản lâu đời nhất là git 2.13.0. Xem [git website](https://www.git-scm.com/) để cập nhật phiên bản git trên local của bạn.
[](https://gitter.im/k88hudson/git-flight-rules?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
**Danh mục nội dung** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Repositories (Kho)](#repositories-kho)
- [Tôi muốn tạo một repository trên local](#t%C3%B4i-mu%E1%BB%91n-t%E1%BA%A1o-m%E1%BB%99t-repository-tr%C3%AAn-local)
- [Tôi muốn clone một remote repository](#t%C3%B4i-mu%E1%BB%91n-clone-m%E1%BB%99t-remote-repository)
- [Tôi để sai remote repository](#t%C3%B4i-%C4%91%E1%BB%83-sai-remote-repository)
- [Tôi muốn thêm sửa code cho repository của người khác](#t%C3%B4i-mu%E1%BB%91n-th%C3%AAm-s%E1%BB%ADa-code-cho-repository-c%E1%BB%A7a-ng%C6%B0%E1%BB%9Di-kh%C3%A1c)
- [Thêm sửa code với pull requests](#th%C3%AAm-s%E1%BB%ADa-code-v%E1%BB%9Bi-pull-requests)
- [Thêm sửa code với các patch (vá)](#th%C3%AAm-s%E1%BB%ADa-code-v%E1%BB%9Bi-c%C3%A1c-patch-v%C3%A1)
- [Tôi cần update fork của tôi với những thay đổi mới nhất từ repository nguyên bản](#t%C3%B4i-c%E1%BA%A7n-update-fork-c%E1%BB%A7a-t%C3%B4i-v%E1%BB%9Bi-nh%E1%BB%AFng-thay-%C4%91%E1%BB%95i-m%E1%BB%9Bi-nh%E1%BA%A5t-t%E1%BB%AB-repository-nguy%C3%AAn-b%E1%BA%A3n)
- [Chỉnh sửa Commit](#ch%E1%BB%89nh-s%E1%BB%ADa-commit)
- [Tôi vừa commit cái gì?](#t%C3%B4i-v%E1%BB%ABa-commit-c%C3%A1i-g%C3%AC)
- [Tôi đã viết sai vài thứ trong message (thông điệp) của commit](#t%C3%B4i-%C4%91%C3%A3-vi%E1%BA%BFt-sai-v%C3%A0i-th%E1%BB%A9-trong-message-th%C3%B4ng-%C4%91i%E1%BB%87p-c%E1%BB%A7a-commit)
- [Tôi đã commit với cấu hình tên và email sai](#t%C3%B4i-%C4%91%C3%A3-commit-v%E1%BB%9Bi-c%E1%BA%A5u-h%C3%ACnh-t%C3%AAn-v%C3%A0-email-sai)
- [Tôi muốn xoá một file từ commit trước](#t%C3%B4i-mu%E1%BB%91n-xo%C3%A1-m%E1%BB%99t-file-t%E1%BB%AB-commit-tr%C6%B0%E1%BB%9Bc)
- [Tôi muốn xoá hoặc loại bỏ commit mới nhất](#t%C3%B4i-mu%E1%BB%91n-xo%C3%A1-ho%E1%BA%B7c-lo%E1%BA%A1i-b%E1%BB%8F-commit-m%E1%BB%9Bi-nh%E1%BA%A5t)
- [Xoá/loại bỏ bất kỳ commit nào](#xo%C3%A1lo%E1%BA%A1i-b%E1%BB%8F-b%E1%BA%A5t-k%E1%BB%B3-commit-n%C3%A0o)
- [Tôi đã cố gắng push commit đã sửa đổi lên remote, nhưng tôi gặp thông báo lỗi](#t%C3%B4i-%C4%91%C3%A3-c%E1%BB%91-g%E1%BA%AFng-push-commit-%C4%91%C3%A3-s%E1%BB%ADa-%C4%91%E1%BB%95i-l%C3%AAn-remote-nh%C6%B0ng-t%C3%B4i-g%E1%BA%B7p-th%C3%B4ng-b%C3%A1o-l%E1%BB%97i)
- [Tôi đã vô tình thực hiện hard reset và tôi muốn các thay đổi của tôi.](#t%C3%B4i-%C4%91%C3%A3-v%C3%B4-t%C3%ACnh-th%E1%BB%B1c-hi%E1%BB%87n-hard-reset-v%C3%A0-t%C3%B4i-mu%E1%BB%91n-c%C3%A1c-thay-%C4%91%E1%BB%95i-c%E1%BB%A7a-t%C3%B4i)
- [Tôi vô tình commit và đẩy lên một merge](#t%C3%B4i-v%C3%B4-t%C3%ACnh-commit-v%C3%A0-%C4%91%E1%BA%A9y-l%C3%AAn-m%E1%BB%99t-merge)
- [Tôi vô tình commit và đẩy các file chứa dữ liệu nhảy cảm](#t%C3%B4i-v%C3%B4-t%C3%ACnh-commit-v%C3%A0-%C4%91%E1%BA%A9y-c%C3%A1c-file-ch%E1%BB%A9a-d%E1%BB%AF-li%E1%BB%87u-nh%E1%BA%A3y-c%E1%BA%A3m)
- [Tôi muốn xóa file to quá để chưa bao giờ xuất hiện trong lịch sử repository](#t%C3%B4i-mu%E1%BB%91n-x%C3%B3a-file-to-qu%C3%A1-%C4%91%E1%BB%83-ch%C6%B0a-bao-gi%E1%BB%9D-xu%E1%BA%A5t-hi%E1%BB%87n-trong-l%E1%BB%8Bch-s%E1%BB%AD-repository)
- [Cách khuyến khích: Sử dụng dịch vụ bên thứ ba bfg](#c%C3%A1ch-khuy%E1%BA%BFn-kh%C3%ADch-s%E1%BB%AD-d%E1%BB%A5ng-d%E1%BB%8Bch-v%E1%BB%A5-b%C3%AAn-th%E1%BB%A9-ba-bfg)
- [Cách có sẵn: Sử dụng git-filter-branch](#c%C3%A1ch-c%C3%B3-s%E1%BA%B5n-s%E1%BB%AD-d%E1%BB%A5ng-git-filter-branch)
- [Bước cuối: Đẩy lịch sử đã thay đổi của repository](#b%C6%B0%E1%BB%9Bc-cu%E1%BB%91i-%C4%91%E1%BA%A9y-l%E1%BB%8Bch-s%E1%BB%AD-%C4%91%C3%A3-thay-%C4%91%E1%BB%95i-c%E1%BB%A7a-repository)
- [Tôi cần thay đổi nội dung của một commit nhưng không phải là cái mới nhất](#t%C3%B4i-c%E1%BA%A7n-thay-%C4%91%E1%BB%95i-n%E1%BB%99i-dung-c%E1%BB%A7a-m%E1%BB%99t-commit-nh%C6%B0ng-kh%C3%B4ng-ph%E1%BA%A3i-l%C3%A0-c%C3%A1i-m%E1%BB%9Bi-nh%E1%BA%A5t)
- [Staging (sân chuyển tiếp)](#staging-s%C3%A2n-chuy%E1%BB%83n-ti%E1%BA%BFp)
- [Tôi muốn nâng lên stage tất cả file đang theo dõi và bỏ qua file không theo dõi](#t%C3%B4i-mu%E1%BB%91n-n%C3%A2ng-l%C3%AAn-stage-t%E1%BA%A5t-c%E1%BA%A3-file-%C4%91ang-theo-d%C3%B5i-v%C3%A0-b%E1%BB%8F-qua-file-kh%C3%B4ng-theo-d%C3%B5i)
- [Chỉ nâng một phần các file đang theo dõi](#ch%E1%BB%89-n%C3%A2ng-m%E1%BB%99t-ph%E1%BA%A7n-c%C3%A1c-file-%C4%91ang-theo-d%C3%B5i)
- [Tôi cần cho thêm các thay đổi đang trong stage vào commit trước](#t%C3%B4i-c%E1%BA%A7n-cho-th%C3%AAm-c%C3%A1c-thay-%C4%91%E1%BB%95i-%C4%91ang-trong-stage-v%C3%A0o-commit-tr%C6%B0%E1%BB%9Bc)
- [Tôi muốn stage một phần của một file mới, nhưng không phải toàn bộ file](#t%C3%B4i-mu%E1%BB%91n-stage-m%E1%BB%99t-ph%E1%BA%A7n-c%E1%BB%A7a-m%E1%BB%99t-file-m%E1%BB%9Bi-nh%C6%B0ng-kh%C3%B4ng-ph%E1%BA%A3i-to%C3%A0n-b%E1%BB%99-file)
- [Tôi muốn thêm các thay đổi trong một file vào 2 commit khác nhau](#t%C3%B4i-mu%E1%BB%91n-th%C3%AAm-c%C3%A1c-thay-%C4%91%E1%BB%95i-trong-m%E1%BB%99t-file-v%C3%A0o-2-commit-kh%C3%A1c-nhau)
- [Tôi cho lên stage quá nhiều thay đổi, và tôi muốn tách ra thành các commit khác nhau](#t%C3%B4i-cho-l%C3%AAn-stage-qu%C3%A1-nhi%E1%BB%81u-thay-%C4%91%E1%BB%95i-v%C3%A0-t%C3%B4i-mu%E1%BB%91n-t%C3%A1ch-ra-th%C3%A0nh-c%C3%A1c-commit-kh%C3%A1c-nhau)
- [Tôi muốn cho lên stage các chỉnh sửa chưa được stage và hã khỏi stage các chỉnh sửa đã stage](#t%C3%B4i-mu%E1%BB%91n-cho-l%C3%AAn-stage-c%C3%A1c-ch%E1%BB%89nh-s%E1%BB%ADa-ch%C6%B0a-%C4%91%C6%B0%E1%BB%A3c-stage-v%C3%A0-h%C3%A3-kh%E1%BB%8Fi-stage-c%C3%A1c-ch%E1%BB%89nh-s%E1%BB%ADa-%C4%91%C3%A3-stage)
- [Thay đổi chưa lên sân (Unstaged Edits)](#thay-%C4%91%E1%BB%95i-ch%C6%B0a-l%C3%AAn-s%C3%A2n-unstaged-edits)
- [Tôi muốn di chuyển các chỉnh sửa chưa lên stage sang một nhánh mới](#t%C3%B4i-mu%E1%BB%91n-di-chuy%E1%BB%83n-c%C3%A1c-ch%E1%BB%89nh-s%E1%BB%ADa-ch%C6%B0a-l%C3%AAn-stage-sang-m%E1%BB%99t-nh%C3%A1nh-m%E1%BB%9Bi)
- [Tôi muốn di chuyển các chỉnh sửa chưa stage của tôi đến một nhánh khác đã tồn tại](#t%C3%B4i-mu%E1%BB%91n-di-chuy%E1%BB%83n-c%C3%A1c-ch%E1%BB%89nh-s%E1%BB%ADa-ch%C6%B0a-stage-c%E1%BB%A7a-t%C3%B4i-%C4%91%E1%BA%BFn-m%E1%BB%99t-nh%C3%A1nh-kh%C3%A1c-%C4%91%C3%A3-t%E1%BB%93n-t%E1%BA%A1i)
- [Tôi muốn bỏ các thay đôi chưa trong commit tại local (đã lên hoặc chưa lên stage)](#t%C3%B4i-mu%E1%BB%91n-b%E1%BB%8F-c%C3%A1c-thay-%C4%91%C3%B4i-ch%C6%B0a-trong-commit-t%E1%BA%A1i-local-%C4%91%C3%A3-l%C3%AAn-ho%E1%BA%B7c-ch%C6%B0a-l%C3%AAn-stage)
- [Tôi muốn loại bỏ các thay đổi cụ thể chưa lên stage](#t%C3%B4i-mu%E1%BB%91n-lo%E1%BA%A1i-b%E1%BB%8F-c%C3%A1c-thay-%C4%91%E1%BB%95i-c%E1%BB%A5-th%E1%BB%83-ch%C6%B0a-l%C3%AAn-stage)
- [Tôi muốn loại bỏ các file cụ thể chưa lên stage](#t%C3%B4i-mu%E1%BB%91n-lo%E1%BA%A1i-b%E1%BB%8F-c%C3%A1c-file-c%E1%BB%A5-th%E1%BB%83-ch%C6%B0a-l%C3%AAn-stage)
- [Tôi muốn chỉ loại bỏ các thay đổi chưa lên stage tại local](#t%C3%B4i-mu%E1%BB%91n-ch%E1%BB%89-lo%E1%BA%A1i-b%E1%BB%8F-c%C3%A1c-thay-%C4%91%E1%BB%95i-ch%C6%B0a-l%C3%AAn-stage-t%E1%BA%A1i-local)
- [Tôi muốn loại bỏ tất cả các file chưa được theo dõi (track)](#t%C3%B4i-mu%E1%BB%91n-lo%E1%BA%A1i-b%E1%BB%8F-t%E1%BA%A5t-c%E1%BA%A3-c%C3%A1c-file-ch%C6%B0a-%C4%91%C6%B0%E1%BB%A3c-theo-d%C3%B5i-track)
- [Tôi muốn hạ khỏi stage một file cụ thể đã stage](#t%C3%B4i-mu%E1%BB%91n-h%E1%BA%A1-kh%E1%BB%8Fi-stage-m%E1%BB%99t-file-c%E1%BB%A5-th%E1%BB%83-%C4%91%C3%A3-stage)
- [Nhánh](#nh%C3%A1nh)
- [Tôi muốn liệt kê tất cả các nhánh](#t%C3%B4i-mu%E1%BB%91n-li%E1%BB%87t-k%C3%AA-t%E1%BA%A5t-c%E1%BA%A3-c%C3%A1c-nh%C3%A1nh)
- [Tạo một nhánh mới từ một commit](#t%E1%BA%A1o-m%E1%BB%99t-nh%C3%A1nh-m%E1%BB%9Bi-t%E1%BB%AB-m%E1%BB%99t-commit)
- [Tôi đã pull (kéo) từ/vào sai nhánh](#t%C3%B4i-%C4%91%C3%A3-pull-k%C3%A9o-t%E1%BB%ABv%C3%A0o-sai-nh%C3%A1nh)
- [Tôi muốn loại bỏ các commit tại local để nhánh của tôi giống như nhánh trên server](#t%C3%B4i-mu%E1%BB%91n-lo%E1%BA%A1i-b%E1%BB%8F-c%C3%A1c-commit-t%E1%BA%A1i-local-%C4%91%E1%BB%83-nh%C3%A1nh-c%E1%BB%A7a-t%C3%B4i-gi%E1%BB%91ng-nh%C6%B0-nh%C3%A1nh-tr%C3%AAn-server)
- [Tôi đã tạo commit lên main thay vì một nhánh mới](#t%C3%B4i-%C4%91%C3%A3-t%E1%BA%A1o-commit-l%C3%AAn-main-thay-v%C3%AC-m%E1%BB%99t-nh%C3%A1nh-m%E1%BB%9Bi)
- [Tôi muốn giữ toàn bộ file từ một ref-ish khác](#t%C3%B4i-mu%E1%BB%91n-gi%E1%BB%AF-to%C3%A0n-b%E1%BB%99-file-t%E1%BB%AB-m%E1%BB%99t-ref-ish-kh%C3%A1c)
- [Tôi đã thực hiện một số commit trên một nhánh mặc dù chúng nên ở các nhánh khác nhau](#t%C3%B4i-%C4%91%C3%A3-th%E1%BB%B1c-hi%E1%BB%87n-m%E1%BB%99t-s%E1%BB%91-commit-tr%C3%AAn-m%E1%BB%99t-nh%C3%A1nh-m%E1%BA%B7c-d%C3%B9-ch%C3%BAng-n%C3%AAn-%E1%BB%9F-c%C3%A1c-nh%C3%A1nh-kh%C3%A1c-nhau)
- [Tôi muốn xóa các nhánh local đã bị xóa tại luồng trước (upstream)](#t%C3%B4i-mu%E1%BB%91n-x%C3%B3a-c%C3%A1c-nh%C3%A1nh-local-%C4%91%C3%A3-b%E1%BB%8B-x%C3%B3a-t%E1%BA%A1i-lu%E1%BB%93ng-tr%C6%B0%E1%BB%9Bc-upstream)
- [Tôi vô tình xóa nhánh của tôi](#t%C3%B4i-v%C3%B4-t%C3%ACnh-x%C3%B3a-nh%C3%A1nh-c%E1%BB%A7a-t%C3%B4i)
- [Tôi muốn xoá một nhánh](#t%C3%B4i-mu%E1%BB%91n-xo%C3%A1-m%E1%BB%99t-nh%C3%A1nh)
- [Tôi muốn xoá nhiều nhánh](#t%C3%B4i-mu%E1%BB%91n-xo%C3%A1-nhi%E1%BB%81u-nh%C3%A1nh)
- [Tôi muốn đổi tên một nhánh](#t%C3%B4i-mu%E1%BB%91n-%C4%91%E1%BB%95i-t%C3%AAn-m%E1%BB%99t-nh%C3%A1nh)
- [Tôi muốn checkout đến một nhánh remote mà người khác đang làm việc trên đó](#t%C3%B4i-mu%E1%BB%91n-checkout-%C4%91%E1%BA%BFn-m%E1%BB%99t-nh%C3%A1nh-remote-m%C3%A0-ng%C6%B0%E1%BB%9Di-kh%C3%A1c-%C4%91ang-l%C3%A0m-vi%E1%BB%87c-tr%C3%AAn-%C4%91%C3%B3)
- [Tôi muốn tạo một nhánh remote mới từ một nhánh local hiện tại](#t%C3%B4i-mu%E1%BB%91n-t%E1%BA%A1o-m%E1%BB%99t-nh%C3%A1nh-remote-m%E1%BB%9Bi-t%E1%BB%AB-m%E1%BB%99t-nh%C3%A1nh-local-hi%E1%BB%87n-t%E1%BA%A1i)
- [Tôi muốn thiết lập một nhánh remote làm upstream (luồng trước) cho một nhánh local](#t%C3%B4i-mu%E1%BB%91n-thi%E1%BA%BFt-l%E1%BA%ADp-m%E1%BB%99t-nh%C3%A1nh-remote-l%C3%A0m-upstream-lu%E1%BB%93ng-tr%C6%B0%E1%BB%9Bc-cho-m%E1%BB%99t-nh%C3%A1nh-local)
- [Tôi muốn để HEAD của tôi dõi theo nhánh mặc định của remote](#t%C3%B4i-mu%E1%BB%91n-%C4%91%E1%BB%83-head-c%E1%BB%A7a-t%C3%B4i-d%C3%B5i-theo-nh%C3%A1nh-m%E1%BA%B7c-%C4%91%E1%BB%8Bnh-c%E1%BB%A7a-remote)
- [Tôi đã thực hiện thay đổi trên sai nhánh](#t%C3%B4i-%C4%91%C3%A3-th%E1%BB%B1c-hi%E1%BB%87n-thay-%C4%91%E1%BB%95i-tr%C3%AAn-sai-nh%C3%A1nh)
- [Tôi muốn tách một nhánh thành hai](#t%C3%B4i-mu%E1%BB%91n-t%C3%A1ch-m%E1%BB%99t-nh%C3%A1nh-th%C3%A0nh-hai)
- [Rebasing và Merging](#rebasing-v%C3%A0-merging)
- [Tôi muốn đảo ngược rebase/merge](#t%C3%B4i-mu%E1%BB%91n-%C4%91%E1%BA%A3o-ng%C6%B0%E1%BB%A3c-rebasemerge)
- [Tôi đã rebase, nhưng tôi không muốn push ép (force push)](#t%C3%B4i-%C4%91%C3%A3-rebase-nh%C6%B0ng-t%C3%B4i-kh%C3%B4ng-mu%E1%BB%91n-push-%C3%A9p-force-push)
- [Tôi cần kết hợp các commit](#t%C3%B4i-c%E1%BA%A7n-k%E1%BA%BFt-h%E1%BB%A3p-c%C3%A1c-commit)
- [Chiến lược merge an toàn](#chi%E1%BA%BFn-l%C6%B0%E1%BB%A3c-merge-an-to%C3%A0n)
- [Tôi cần merge một nhánh thành một commit duy nhất](#t%C3%B4i-c%E1%BA%A7n-merge-m%E1%BB%99t-nh%C3%A1nh-th%C3%A0nh-m%E1%BB%99t-commit-duy-nh%E1%BA%A5t)
- [Tôi chỉ muốn kết hợp các commit chưa push](#t%C3%B4i-ch%E1%BB%89-mu%E1%BB%91n-k%E1%BA%BFt-h%E1%BB%A3p-c%C3%A1c-commit-ch%C6%B0a-push)
- [Tôi cần huỷ bỏ merge](#t%C3%B4i-c%E1%BA%A7n-hu%E1%BB%B7-b%E1%BB%8F-merge)
- [Tôi cần cập nhật commit gốc (parent commit) cho nhánh của tôi](#t%C3%B4i-c%E1%BA%A7n-c%E1%BA%ADp-nh%E1%BA%ADt-commit-g%E1%BB%91c-parent-commit-cho-nh%C3%A1nh-c%E1%BB%A7a-t%C3%B4i)
- [Kiểm tra xem tất cả commit trên một nhánh đã được merge](#ki%E1%BB%83m-tra-xem-t%E1%BA%A5t-c%E1%BA%A3-commit-tr%C3%AAn-m%E1%BB%99t-nh%C3%A1nh-%C4%91%C3%A3-%C4%91%C6%B0%E1%BB%A3c-merge)
- [Các vấn đề có thể xảy ra với interactive rebase](#c%C3%A1c-v%E1%BA%A5n-%C4%91%E1%BB%81-c%C3%B3-th%E1%BB%83-x%E1%BA%A3y-ra-v%E1%BB%9Bi-interactive-rebase)
- [Màn hình chỉnh sửa rebase ghi 'noop'](#m%C3%A0n-h%C3%ACnh-ch%E1%BB%89nh-s%E1%BB%ADa-rebase-ghi-noop)
- [Có một vài xung đột](#c%C3%B3-m%E1%BB%99t-v%C3%A0i-xung-%C4%91%E1%BB%99t)
- [Stash (Cất)](#stash-c%E1%BA%A5t)
- [Stash tất cả chỉnh sửa](#stash-t%E1%BA%A5t-c%E1%BA%A3-ch%E1%BB%89nh-s%E1%BB%ADa)
- [Stash các file cụ thể](#stash-c%C3%A1c-file-c%E1%BB%A5-th%E1%BB%83)
- [Stash với message (thông điệp)](#stash-v%E1%BB%9Bi-message-th%C3%B4ng-%C4%91i%E1%BB%87p)
- [Apply một stash cụ thể từ danh sách](#apply-m%E1%BB%99t-stash-c%E1%BB%A5-th%E1%BB%83-t%E1%BB%AB-danh-s%C3%A1ch)
- [Stash trong khi giữ các thay đổi chưa stage](#stash-trong-khi-gi%E1%BB%AF-c%C3%A1c-thay-%C4%91%E1%BB%95i-ch%C6%B0a-stage)
- [Finding (Tìm)](#finding-t%C3%ACm)
- [Tôi muốn tìm một chuỗi ký tự trong bất kỳ commit nào](#t%C3%B4i-mu%E1%BB%91n-t%C3%ACm-m%E1%BB%99t-chu%E1%BB%97i-k%C3%BD-t%E1%BB%B1-trong-b%E1%BA%A5t-k%E1%BB%B3-commit-n%C3%A0o)
- [Tôi muốn tìm tác giả hoặc người commit](#t%C3%B4i-mu%E1%BB%91n-t%C3%ACm-t%C3%A1c-gi%E1%BA%A3-ho%E1%BA%B7c-ng%C6%B0%E1%BB%9Di-commit)
- [Tôi muốn liệt kê các commit chứa các file cụ thể](#t%C3%B4i-mu%E1%BB%91n-li%E1%BB%87t-k%C3%AA-c%C3%A1c-commit-ch%E1%BB%A9a-c%C3%A1c-file-c%E1%BB%A5-th%E1%BB%83)
- [Tôi muốn xem lịch sử commit của một function (chức năng) cụ thể](#t%C3%B4i-mu%E1%BB%91n-xem-l%E1%BB%8Bch-s%E1%BB%AD-commit-c%E1%BB%A7a-m%E1%BB%99t-function-ch%E1%BB%A9c-n%C4%83ng-c%E1%BB%A5-th%E1%BB%83)
- [Tìm một tag mà một commit đã tham chiếu](#t%C3%ACm-m%E1%BB%99t-tag-m%C3%A0-m%E1%BB%99t-commit-%C4%91%C3%A3-tham-chi%E1%BA%BFu)
- [Submodules](#submodules)
- [Clone tất cả submodules](#clone-t%E1%BA%A5t-c%E1%BA%A3-submodules)
- [Xoá một submodule](#xo%C3%A1-m%E1%BB%99t-submodule)
- [Miscellaneous Objects (Những thứ khác)](#miscellaneous-objects-nh%E1%BB%AFng-th%E1%BB%A9-kh%C3%A1c)
- [Copy thư mục hoặc tệp file từ một nhánh sang nhánh khác](#copy-th%C6%B0-m%E1%BB%A5c-ho%E1%BA%B7c-t%E1%BB%87p-file-t%E1%BB%AB-m%E1%BB%99t-nh%C3%A1nh-sang-nh%C3%A1nh-kh%C3%A1c)
- [Khôi phục một file đã bị xoá](#kh%C3%B4i-ph%E1%BB%A5c-m%E1%BB%99t-file-%C4%91%C3%A3-b%E1%BB%8B-xo%C3%A1)
- [Xoá tag](#xo%C3%A1-tag)
- [Khôi phục một tag đã bị xoá](#kh%C3%B4i-ph%E1%BB%A5c-m%E1%BB%99t-tag-%C4%91%C3%A3-b%E1%BB%8B-xo%C3%A1)
- [Patch (Vá) bị xóa](#patch-v%C3%A1-b%E1%BB%8B-x%C3%B3a)
- [Xuất một repository ra một file Zip](#xu%E1%BA%A5t-m%E1%BB%99t-repository-ra-m%E1%BB%99t-file-zip)
- [Push một nhánh và một tag có tên giống nhau](#push-m%E1%BB%99t-nh%C3%A1nh-v%C3%A0-m%E1%BB%99t-tag-c%C3%B3-t%C3%AAn-gi%E1%BB%91ng-nhau)
- [Tracking (Theo dõi) các file](#tracking-theo-d%C3%B5i-c%C3%A1c-file)
- [Tôi muốn thay đổi cách viết hoa của tên tệp mà không thay đổi nội dung của tệp](#t%C3%B4i-mu%E1%BB%91n-thay-%C4%91%E1%BB%95i-c%C3%A1ch-vi%E1%BA%BFt-hoa-c%E1%BB%A7a-t%C3%AAn-t%E1%BB%87p-m%C3%A0-kh%C3%B4ng-thay-%C4%91%E1%BB%95i-n%E1%BB%99i-dung-c%E1%BB%A7a-t%E1%BB%87p)
- [Tôi muốn ghi đè lên các tệp local khi thực hiện lệnh git pull](#t%C3%B4i-mu%E1%BB%91n-ghi-%C4%91%C3%A8-l%C3%AAn-c%C3%A1c-t%E1%BB%87p-local-khi-th%E1%BB%B1c-hi%E1%BB%87n-l%E1%BB%87nh-git-pull)
- [Tôi muốn xóa một tệp khỏi Git nhưng vẫn giữ tệp](#t%C3%B4i-mu%E1%BB%91n-x%C3%B3a-m%E1%BB%99t-t%E1%BB%87p-kh%E1%BB%8Fi-git-nh%C6%B0ng-v%E1%BA%ABn-gi%E1%BB%AF-t%E1%BB%87p)
- [Tôi muốn đảo ngược tệp về bản sửa đổi cụ thể](#t%C3%B4i-mu%E1%BB%91n-%C4%91%E1%BA%A3o-ng%C6%B0%E1%BB%A3c-t%E1%BB%87p-v%E1%BB%81-b%E1%BA%A3n-s%E1%BB%ADa-%C4%91%E1%BB%95i-c%E1%BB%A5-th%E1%BB%83)
- [Tôi muốn liệt kê các thay đổi của một tệp cụ thể giữa các commit hoặc các nhánh](#t%C3%B4i-mu%E1%BB%91n-li%E1%BB%87t-k%C3%AA-c%C3%A1c-thay-%C4%91%E1%BB%95i-c%E1%BB%A7a-m%E1%BB%99t-t%E1%BB%87p-c%E1%BB%A5-th%E1%BB%83-gi%E1%BB%AFa-c%C3%A1c-commit-ho%E1%BA%B7c-c%C3%A1c-nh%C3%A1nh)
- [Tôi muốn Git bỏ qua những thay đổi đối với một tệp cụ thể](#t%C3%B4i-mu%E1%BB%91n-git-b%E1%BB%8F-qua-nh%E1%BB%AFng-thay-%C4%91%E1%BB%95i-%C4%91%E1%BB%91i-v%E1%BB%9Bi-m%E1%BB%99t-t%E1%BB%87p-c%E1%BB%A5-th%E1%BB%83)
- [Debugging (Gỡ lỗi) with Git](#debugging-g%E1%BB%A1-l%E1%BB%97i-with-git)
- [Cấu hình (Configuration)](#c%E1%BA%A5u-h%C3%ACnh-configuration)
- [Tôi muốn thêm bí danh (alias) cho một số lệnh Git](#t%C3%B4i-mu%E1%BB%91n-th%C3%AAm-b%C3%AD-danh-alias-cho-m%E1%BB%99t-s%E1%BB%91-l%E1%BB%87nh-git)
- [Tôi muốn thêm một thư mục trống vào repository của tôi](#t%C3%B4i-mu%E1%BB%91n-th%C3%AAm-m%E1%BB%99t-th%C6%B0-m%E1%BB%A5c-tr%E1%BB%91ng-v%C3%A0o-repository-c%E1%BB%A7a-t%C3%B4i)
- [Tôi muốn cache (cho vào bộ nhớ đệm) một username và password cho một repository](#t%C3%B4i-mu%E1%BB%91n-cache-cho-v%C3%A0o-b%E1%BB%99-nh%E1%BB%9B-%C4%91%E1%BB%87m-m%E1%BB%99t-username-v%C3%A0-password-cho-m%E1%BB%99t-repository)
- [Tôi muốn Git bỏ qua các quyền và thay đổi về filemode (chế độ file)](#t%C3%B4i-mu%E1%BB%91n-git-b%E1%BB%8F-qua-c%C3%A1c-quy%E1%BB%81n-v%C3%A0-thay-%C4%91%E1%BB%95i-v%E1%BB%81-filemode-ch%E1%BA%BF-%C4%91%E1%BB%99-file)
- [Tôi muốn đặt người dùng toàn cục (global user)](#t%C3%B4i-mu%E1%BB%91n-%C4%91%E1%BA%B7t-ng%C6%B0%E1%BB%9Di-d%C3%B9ng-to%C3%A0n-c%E1%BB%A5c-global-user)
- [Tôi không biết mình đã làm gì sai](#t%C3%B4i-kh%C3%B4ng-bi%E1%BA%BFt-m%C3%ACnh-%C4%91%C3%A3-l%C3%A0m-g%C3%AC-sai)
- [Git Shortcuts (phím tắt)](#git-shortcuts-ph%C3%ADm-t%E1%BA%AFt)
- [Git Bash](#git-bash)
- [PowerShell trên Windows](#powershell-tr%C3%AAn-windows)
- [Tài nguyên khác](#t%C3%A0i-nguy%C3%AAn-kh%C3%A1c)
- [Sách](#s%C3%A1ch)
- [Hướng dẫn](#h%C6%B0%E1%BB%9Bng-d%E1%BA%ABn)
- [Scripts (tập lệnh) và các công cụ](#scripts-t%E1%BA%ADp-l%E1%BB%87nh-v%C3%A0-c%C3%A1c-c%C3%B4ng-c%E1%BB%A5)
- [GUI Clients](#gui-clients)
## Repositories (Kho)
### Tôi muốn tạo một repository trên local
Để tạo một Git repository tại thư mục đã tồn tại:
```sh
(thư-mục-của-tôi) $ git init
```
### Tôi muốn clone một remote repository
Để clone (copy) một remote repository, copy đường dẫn url cho repository, và chạy :
```sh
$ git clone [url]
```
Lệnh này sẽ tải xuống một thư mục có tên giống tên của remote repository. Hãy chắc chắn rằng bạn có kết nối đến remote server khi bạn đang clone về (phần lớn thời gian nghĩa là cần đảm bảo bạn kết nối được với internet).
Để clone vào một thư mục với tên khác với tên mặc định của repository:
```sh
$ git clone [url] name-of-new-folder
```
### Tôi để sai remote repository
Có thể có vài vấn đề khác nhau:
Nếu bạn clone sai repository, chỉ cần xóa thư mục tạo bởi `git clone` và sau đó clone đúng remote repository.
Nếu bạn để nhầm repository là origin của một local repository hiện tại, thay đổi URL của origin với lệnh:
```sh
$ git remote set-url origin [url của repo đúng]
```
Xem thêm tại [StackOverflow](https://stackoverflow.com/questions/2432764/how-to-change-the-uri-url-for-a-remote-git-repository#2432799).
### Tôi muốn thêm sửa code cho repository của người khác
Git không cho bạn thêm sửa code vào repository của người khác nếu không có quyền truy cập. GitHub cũng thế, GitHub khác với Git vì là dịch vụ hosting cho các Git repository. Nhưng bạn có thể thêm sửa code với các patch vá lỗi, hoặc, nếu trên GitHub, với forks và pull requests.
Trước hêt, một vài điều về fork. Một fork là một copy của một repository. Đây không phải là một lệnh git, mà là một hành động thường thấy trên GitHub, Bitbucket, GitLab — hoặc bắt cứ đâu host các Git repository. Bạn có thể fork một repository qua UI của dịch vụ host.
#### Thêm sửa code với pull requests
Sau khi đã fork một repository, bạn thường phải clone repository về máy của bạn. Tất nhiên bạn có thể tạo vài chỉnh sửa nhỏ trên GitHub nếu không clone về máy, nhưng văn bản này không phải là github-flight-rules, thế nên hãy xem cách trên máy local.
```sh
# nếu bạn dùng ssh
$ git clone git@github.com:k88hudson/git-flight-rules.git
# nếu bạn dùng https
$ git clone https://github.com/k88hudson/git-flight-rules.git
```
Nếu bạn `cd` vào thư mục được tạo, và chạy lệnh `git remote`, bạn sẽ thấy danh sách các remote. Thường sẽ có một remote - `origin` - trỏ đến `k88hudson/git-flight-rules`. Trong trường hợp này, bạn cũng muốn một remote trỏ đến fork của bạn.
Đầu tiên, để theo quy chuẩn dùng Git, chúng ta sẽ dùng remote tên `origin` cho repository của bạn và tên `upstream` cho repository mà bạn fork. Để đổi tên cho remote `origin` sang tên `upstream` chạy lệnh:
```sh
$ git remote rename origin upstream
```
Bạn cũng có thể đổi tên với lệnh `git remote set-url`, nhưng sẽ mất thêm thời gian và nhiều bước hơn.
Sau đó, tạo remote mới để trỏ về repository của bạn.
```sh
$ git remote add origin git@github.com:YourtGitHubUsername/git-flight-rules.git
```
Lưu ý là bây giờ bạn có hai remote.
- `origin` trỏ đến repository của bạn.
- `upstream` trỏ đến repository nguyên bản .
Với `origin`, bạn có thể đọc và viết. Với `upstream`, bạn chỉ có thể đọc.
Sau khi đã chỉnh sửa theo mong muốn, push (đẩy) các thay đổi (thường là ở trong branch) tới remote tên `origin`. Nếu bạn ở trên nhánh, bạn có thể dùng `--set-upstream` để tránh cần phải ghi rõ dùng brach nào của remote mỗi lần push trong tương lai khi dùng nhánh đấy. Ví dụ:
```sh
$ (feature/my-feature) git push --set-upstream origin feature/my-feature
```
Không có cách nào để tạo pull request trên giao diện lệnh (CLI) với Git (mặc dù có vài công cụ, như [hub](http://github.com/github/hub), có cho bạn lựa chọn này). Nếu bạn sãn sàng tạo Pull Request, trở lại GitHub (hoặc dịch vụ host Git) và tạo pull request mới. Nhớ là dịch vụ host sẽ tự động link repository nguyên bản và repository do fork.
Sau cùng, nhớ đùng quên trả lời những comment phê duyệt code.
#### Thêm sửa code với các patch (vá)
Một cách khác để thêm sửa code mà không cần sử dụng dịch vụ bên thứ ba như GitHub là dùng `git format-patch`.
`format-patch` tạo file (tệp) dạng .patch cho một hoặc nhiều commit. File này là cơ bản là danh sách nhưng thay đổi, giống như những commit diffs bạn xem được trên Github.
Các patch có thể được xem hoặc thậm chí thêm sửa bởi người nhận và áp gắn với lệnh `git am`.
Ví dụ, để tạo patch dựa vào commit mới nhât, bạn chạy lệnh `git format-patch HEAD^`, lệnh sẽ tạo một tệp .patch với tên như: `0001-My-Commit-Message.patch`.
Để áp gắn tệp patch cho repository, bạn sẽ dùng lệnh `git am ./0001-My-Commit-Message.patch`.
Các patch còn có thể gửi qua email với lệnh `git send-email`. Để xem thêm thông tin về cách dùng hoặc cấu hình, xem: https://git-send-email.io
#### Tôi cần update fork của tôi với những thay đổi mới nhất từ repository nguyên bản
Sau một quãng thời gian, kho `upstream` có thể có thêm thay đổi, và những thay đổi này cần phải được tải về kho `origin`. Nhớ là giống bạn, những người khác cũng đang góp sức của họ. Giả dụ bạn đang ở nhánh cho tính năng mới bạn đang thiết kế, và bạn cần update nhánh với những thay đổi trên repository nguyên bản.
Có khi bạn đã có remote trỏ đến project nguyên bản. Nếu không, hãy tạo nó. Thường chúng ta dùng tên `upstream` cho remote này:
```sh
$ (main) git remote add upstream
# $ (main) git remote add upstream git@github.com:k88hudson/git-flight-rules.git
```
Bây giờ bạn fetch (lấy) từ `upstream` và nhận những update mới nhất.
```sh
$ (main) git fetch upstream
$ (main) git merge upstream/main
# hoặc với một lệnh duy nhất
$ (main) git pull upstream main
```
## Chỉnh sửa Commit
### Tôi vừa commit cái gì?
Giả sử bạn vừa commit những thay đổi một cách mù quáng với lệnh `git commit -a` và bạn không chắc chắn nội dung thực sự của commit vừa thực hiện là gì. Bạn có thể hiển thị ra commit gần nhất trên trỏ HEAD hiện tại của bạn với lệnh:
```sh
(main)$ git show
```
Hoặc
```sh
$ git log -n1 -p
```
Nếu bạn muốn xem một file tại một commit cụ thể, bạn cũng có thể làm được điều này (khi `` là commit mà bạn muốn biết) với lệnh:
```sh
$ git show :filename
```
### Tôi đã viết sai vài thứ trong message (thông điệp) của commit
Nếu bạn đã viết sai thứ gì đó và commit chưa được push lên, bạn có thể làm theo cách sau để thay đổi message của commit mà không làm thay đổi commit:
```sh
$ git commit --amend --only
```
Câu lệnh đó sẽ mở trình soạn thảo (text editor) mặc định của bạn, nơi bạn có thể chỉnh sửa message. Ngoài ra, bạn có thể làm tất cả điều này với lệnh sau:
```sh
$ git commit --amend --only -m 'xxxxxxx'
```
Nếu bạn đã đẩy message lên, bạn có thể chỉnh sửa commit và force push (đẩy ép), nhưng cách này không được khuyến khích.
### Tôi đã commit với cấu hình tên và email sai
Nếu đó là một commit độc lập, chỉnh sửa nó:
```sh
$ git commit --amend --no-edit --author "TênTácGiảMới "
```
Một cách khác để cấu hình đúng tác giả là cài đặt lại với lệnh `git config --global author.(name|email)` và sau đó chạy lệnh
```sh
$ git commit --amend --reset-author --no-edit
```
Nếu bạn cần thay đổi tất cả lịch sử, hãy xem trang `man` của `git filter-branch`.
### Tôi muốn xoá một file từ commit trước
Để xoá các thay đổi đối với một file khỏi commit trước, hãy làm như sau:
```sh
$ git checkout HEAD^ myfile
$ git add myfile
$ git commit --amend --no-edit
```
Trong trường hợp file mới được thêm vào commit và bạn muốn xoá nó (riêng trên Git), hãy thực hiện:
```sh
$ git rm --cached myfile
$ git commit --amend --no-edit
```
Cách này đăc biệt hữu ích khi bạn đang mở một bản patch và bạn đã commit một file không cần thiết và cần force push để cập nhật bản patch trên remote. Dòng `--no-edit` được dùng để giữ không thay đổi message cho commit hiện tại.
### Tôi muốn chuyển một thay đổi từ commit sang commit khác
Nếu bạn đã thực hiện một commit bao gồm các thay đổi phù hợp hơn với một commit khác, bạn có thể di chuyển các thay đổi sang commit khác bằng cách sử dụng rebase tương tác (interactive rebase). Đó là câu trả lời từ [stackoverflow](https://stackoverflow.com/a/54985304/2491502).
Ví dụ: bạn có ba commit (a, b, c). Trên b, bạn đã thay đổi file1 và file2 và bạn muốn chuyển thay đổi trên file1 từ commit b sang commit a.
Đầu tiên, rebase interactively:
```sh
$ git rebase -i HEAD~3
```
Editor sẽ hiện lên như bên dưới:
```sh
pick a
pick b
pick c
```
Sửa 2 dòng với a và b thành edit:
```sh
edit a
edit b
pick c
```
Lưu và đóng Editor. Bạn sẽ được đưa tới commit b. Bây giờ, hãy reset các thay đổi của file1:
```sh
$ git reset HEAD~1 file1
```
Thao tác trên sẽ unstash những thay đổi trong file1. Tiếp tục, stash những thay đổi đó và tiếp tục rebase:
```sh
$ git stash
$ git rebase --continue
```
Bây giờ bạn sẽ quay lại chỉnh sửa commit a. Unstash các thay đổi sau đó thêm chúng vào commit hiện tại và tiếp tục rebase:
```sh
$ git stash pop
$ git add file1
$ git commit --amend --no-edit
$ git rebase --continue
```
Bây giờ quá trình rebase của bạn đã hoàn tất, với những thay đổi từ b trên a. Nếu bạn muốn di chuyển các thay đổi từ b sang c, bạn sẽ phải thực hiện hai lần rebase vì c đứng trước b: một để lấy các thay đổi ra khỏi b, sau đó một để chỉnh sửa c và thêm các thay đổi đã stash.
### Tôi muốn xoá hoặc loại bỏ commit mới nhất
Nếu bạn muốn xoá các commit đã push, bạn có thể làm như sau. Tuy nhiên, cách này sẽ thay đổi lịch sử commit không thay đổi được và làm hỏng lịch sử của bất kỳ ai khác đã pull từ repository. Tóm lại, nếu bạn không chắc chắn, bạn không bao giờ nên làm cách này.
```sh
$ git reset HEAD^ --hard
$ git push --force-with-lease [remote] [branch]
```
Nếu bạn chưa push, để đảo ngược Git về trạng thái trước khi bạn thực hiện commit mới nhất (trong khi vãn giữ các thay đổi trong stage) hãy chạy lệnh:
```
(my-branch)$ git reset --soft HEAD^
```
Cách này chỉ phù hợp nếu bạn chưa push. Nếu bạn đã push, điều thực sự an toàn nhất cần làm là `git revert SHAcủaCommitSai`. Lệnh này sẽ tạo một commit mới để quay trở lại thay đổi của commit trước đó. Hoặc nếu nhánh bạn đã push là rebase-safe (không có kỳ vọng các dev khác sẽ pull từ nó), bạn chỉ có thể sử dụng `git push --force-with-lease`. Để biết thêm, hãy xem [phần trên](#delete-pushed-commit).
### Xoá/loại bỏ bất kỳ commit nào
Lưu ý như trên. Không bao giờ làm điều này nếu có thể tránh được.
```sh
$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push --force-with-lease [remote] [branch]
```
Hoặc thực hiện một [interactive rebase](#interactive-rebase) và loại bỏ các dòng tương ứng với các commit bạn muốn loại bỏ.
### Tôi đã cố gắng push commit đã sửa đổi lên remote, nhưng tôi gặp thông báo lỗi
```sh
To https://github.com/yourusername/repo.git
! [rejected] mybranch -> mybranch (non-fast-forward)
error: failed to push some refs to 'https://github.com/tanay1337/webmaker.org.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```
Lưu ý rằng, như với rebase (xem bên dưới), amend **thay thế commit cũ với một commit mới**, nên bạn phải force push (`--force-with-lease`) các thay đổi của bạn nếu bạn đã push commit trước amend lên remote của bạn. Hãy cẩn thận khi bạn cách này – *luôn luôn* đảm bảo rằng bạn đã chỉ định một nhánh!
```sh
(my-branch)$ git push origin mybranch --force-with-lease
```
Nói chung, **tránh force push**. Tốt nhất là tạo và push một commit mới thay vì force-push commit đã sửa đổi vì nó sẽ gây xung đột trong lịch sử commit cho bất kỳ developer nào đã tương tác với nhánh được đề cập hoặc bất kỳ nhánh con nào. `--force-with-lease` sẽ vẫn fail, nếu ai khác cũng đang làm việc trên cùng một nhánh với bạn và việc push lên sẽ ép trên những thay đổi đó.
Nếu bạn *hoàn toàn chắc chắn* rằng không ai đang làm việc trên cùng một nhánh hoặc bạn muốn cập nhật đỉnh nhánh (tip of branch) *vô điều kiện*, bạn có thể sử dụng `--force` (`-f`), nhưng cách này nói chung nên tránh.
### Tôi đã vô tình thực hiện hard reset và tôi muốn các thay đổi của tôi.
Nếu vô tình bạn thực hiện `git reset --hard`, bạn có thể vẫn phục hồi lại được commit của bạn, vì git giữ một bản log cho tất cả mọi thứ trong vài ngày.
Chú ý: Điều này chỉ hợp lệ nếu đã có sao lưu, tức là đã có commit hoặc được `stash`. Lệnh `git reset --hard` *sẽ loại bỏ* các thay đổi chưa được commit, vì vậy hãy sử dụng nó một cách thận trọng. (Một lựa chọn an toàn là `git reset --keep`.)
```sh
(main)$ git reflog
```
Bạn sẽ thấy danh sách các commit gần đây và một commit để reset. Chọn SHA của commit bạn muốn trở lại tới và reset lại:
```sh
(main)$ git reset --hard SHA1234
```
Thế này là xong.
### Tôi vô tình commit và đẩy lên một merge
Nếu bạn vô tình merge một nhánh tính năng mới vào nhánh phát triển chính trước khi sẵn sàng để merge, bạn vẫn có thể đảo ngược merge. Nhưng có một điểm phải nắm được: Một commit merge có một hoặc nhiều hơn một parent (gốc) (thường là 2).
Lệnh để chạy:
```sh
(feature-branch)$ git revert -m 1
```
Dòng `-m 1` là để cho biết cần chọn parent thứ nhất` (nhánh mà merge được thực hiện) làm parent để đảo ngược lại.
Chú ý: Số parent không phải là số commit. Thay vào đó, một commit merge sẽ có một dòng như `Merge: 8e2ce2d 86ac2e7`. Số parent là số số nhận dạng đầu-1 (1-based index) của dòng nay, số nhận dạng đầu tiên là 1 cho parent thứ nhất, thứ 2 là cho parent 2, và tiếp tục như thế.
### Tôi vô tình commit và đẩy các file chứa dữ liệu nhảy cảm
Nếu bạn vô tình push lên các file chứa dữ liệu nhạy cảm (mật khẩu, keys, etc.), bạn có thể amend commit trước. Lưu ý rằng khi bạn đã đẩy một commit, bạn nên coi bất kỳ dữ liệu nào đã bị đẩy như đã bị lộ. Các bước này có thể xoá dữ liệu nhạy cảm từ repo công khai (public repo) hoặc bản sao nội bộ, nhưng bạn *không thể* xóa dữ liệu nhạy cảm khỏi các bản sao đã được tải về bởi người khác. Nếu bạn có commit mật khẩu, *hãy thay đổi mật khẩu ngay lập tức*. Nếu bạn đã commit một key, *hãy tạo lại key đó ngay lập tức*. Việc amend commit đã đẩy là không đủ, vì bất kỳ ai cũng có thể đã pull commit chứa dữ liệu nhạy cảm của bạn trong thời gian đấy.
Nếu bạn đã chỉnh sửa tệp và xóa dữ liệu nhạy cảm, hãy chạy
```sh
(feature-branch)$ git add EditedFile
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
Nếu bạn muốn xóa toàn bộ tệp (nhưng giữ trên local), hãy chạy:
```sh
(feature-branch)$ git rm --cached sensitive_file
echo sensitive_file >> .gitignore
(feature-branch)$ git add .gitignore
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
Ngoài ra, lưu trữ dữ liệu nhạy cảm của bạn trong các biến môi trường (variable) của local.
Nếu bạn muốn xóa hoàn toàn toàn bộ tệp (và không giữ tệp tại local), hãy chạy
```sh
(feature-branch)$ git rm sensitive_file
(feature-branch)$ git commit --amend --no-edit
(feature-branch)$ git push --force-with-lease origin [branch]
```
Nếu bạn đã thực hiện các commit khác (tức là dữ liệu nhạy cảm nằm tại commit trước commit mới nhất), bạn sẽ phải rebase.
### Tôi muốn xóa file to quá để chưa bao giờ xuất hiện trong lịch sử repository
Nếu file bạn muốn xóa cần bảo mật hay là file chưa thông tin nhạy cảm, xem phần [xóa file chứa thông tin nhạy cảm](#undo-sensitive-commit-push).
Mặc dù bạn đã xóa một file to hay file không muốn có trong dự án, nó có thể vẫn tồn tại trong lịch sử git (git history) của respository trong thư mục `.git`, và sẽ khiến các lệnh `git clone` tải file không cần thiết.
Những bước trong phần này sẽ yêu cầu push ép, và viết lại phần nào lịch sử git của repository, thế nên nếu bạn làm việc với những người khác, kiểm tra là những thay đổi của họ đã được đẩy.
Có hai lựa chọn để viết lại lịch sử, sử dụng tính năng sãn có `git-filter-branch` hoặc dùng [`bfg-repo-cleaner`](https://rtyley.github.io/bfg-repo-cleaner/). `bfg` thao tác sạch hơn và nhanh hơn, nhưng đây là phần mềm bên thứ ba và cần có Java. Chúng ta sẽ xem cả hai cách. Bước cuối cùng là push ép thay đổi của bạn, lần này sẽ còn cần chú ý xem xét hơn các push ép bình thường bởi vì một phần không nhỏ lịch sử repository sẽ thay đổi vĩnh viễn.
#### Cách khuyến khích: Sử dụng dịch vụ bên thứ ba bfg
Sử dụng bfg-repo-cleaner cần có Java. Tải file dạng .jar cho phần mềm bfg với đường link [này](https://rtyley.github.io/bfg-repo-cleaner/). Ví dụ tại đây sẽ dùng `bfg.jar`, nhưng file bạn tải xuống có thể có thêm số phiên bản như `bfg-1.13.0.jar`.
Để xóa một file, dùng lệnh:
```sh
(main)$ git rm path/to/FileToRemove
(main)$ git commit -m "Commit removing filetoremove"
(main)$ java -jar ~/Downloads/bfg.jar --delete-files FileToRemove
```
Lưu ý là với bfg bạn dùng tển của file chứ không phải đường dẫn đến file.
Bạn cũng có thể xóa file dượng theo một khuôn mẫu, ví dụ xóa tất cả file dạng .jpg:
```sh
(main)$ git rm *.jpg
(main)$ git commit -m "Commit removing *.jpg"
(main)$ java -jar ~/Downloads/bfg.jar --delete-files *.jpg
```
Với bfg, the files that exist on your latest commit will not be affected. For example, if you had several large .tga files in your repo, and then in an earlier commit, you deleted a subset of them, this call does not touch files present in the latest commit
Lưu ý, nếu bạn thay đổi tên file trong một commit trước, ví dụ: nếu tệp bắt đầu với tên `LargeFileFirstName.mp4` và một commit đổi tên tệp thành `LargeFileSecondName.mp4`, chạy lệnh `java -jar ~/Downloads/bfg.jar --delete-files LargeFileSecondName.mp4` sé không xóa file trong lịch sử git. Hoặc là chạy lệnh `--delete-files` với cả hai tên, hoặc với khuôn mẫu như trên.
#### Cách có sẵn: Sử dụng git-filter-branch
`git-filter-branch` nặng hơn và ít tính năng hơn, nhưng bạn có thể dùng cách này nếu không thể cài hay chạy `bfg`.
Trong lệnh bên dưới, thay `filepattern` với tên file hoặc khuông mẫu, v.d. `*.jpg`. Lệnh này sẽ xóa file theo khuôn mẫu khỏi tất cả lịch sử của tất cả các nhánh.
```sh
(main)$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch filepattern' --prune-empty --tag-name-filter cat -- --all
```
Giải thích lệnh trên:
`--tag-name-filter cat` khá là nặng, nhưng là cách đơn giản nhất để giữ nguyên các tags cho các commit mới bằng cách sử dụng lệnh `cat`.
`--prune-empty` xóa những commit bây giờ để trống rỗng.
#### Bước cuối: Đẩy lịch sử đã thay đổi của repository
Một khi bạn đã xóa file, kiểm tra thật cẩn thận là bạn không làm hỏng cái gì trong repo - và nếu bạn đã làm hỏng cái gì đó, dễ nhất là clone repo lại và bắt đầu từ đầu.
Để kết thúc, bạn có thể dùng chức năng thu hồi rác (garbage collection) để giảm thiểu kích cỡ tệp .git và rồi push ép. c
```sh
(main)$ git reflog expire --expire=now --all && git gc --prune=now --aggressive
(main)$ git push origin --force --tags
```
Vì bạn vừa viết lại toàn bộ lịch sử git repository, lệnh `git push` có thể quá to để thi hành, và gửi lại thông điệp lỗi (error) `“The remote end hung up unexpectedly”`. Nếu việc này xảy ra, bạn có thể thử tăng post buffer của git:
```sh
(main)$ git config http.postBuffer 524288000
(main)$ git push --force
```
Nếu cách này không hiệu quả, bạn sẽ phải push thủ công lịch sử repo từng cục một. Với lệnh bên dưới, dần dần tăng con số cho `` đến khi nào lệnh push thành công.
```sh
(main)$ git push -u origin HEAD~:refs/head/main --force
```
Một khi lệnh push thành công, dần dần giảm thiểu `` cho đến khi một lệnh `git push` bình thường thành công.
### Tôi cần thay đổi nội dung của một commit nhưng không phải là cái mới nhất
Giả sử bạn đã có vài (v.d. ba) commit và sau nhận ra là bạn quên mất không cho vào một thứ gì đó hợp hơn với commit đầu tiên. Việc này làm phiền bạn vì mặc dù nếu tiếp tục commit bạn sẽ có lịch sử sạch sẽ nhưng commit của bạn không nguyên chất (những thay đổi liên quan với nhau nên ở cùng một commit). Trong trường hợp như vậy, bạn chắc muốn cho thêm những thay đổi liên quan vào commit mong muốn nhưng không muốn những commit sau tiếp cũng phải sửa theo. Trong trường hợp như vây, `git rebase` có thể cứu bạn.
Hay xem trường hợp mà bạn muốn thay đổi commit số ba nếu đếm ngược.
```sh
(nhánh-bạn)$ git rebase -i HEAD~4
```
Lệnh trên đưa bạn vào mode (chế độ) rebase tương tác (interactive rebase), chế độ cho phép bạn edit ba commit mới nhất. Một trình soạn thảo (text editor) sẽ bật lên trông giống như sau:
```sh
pick 9e1d264 commit trước ba
pick 4b6e19a commit trước hai
pick f4037ec commit trước
```
và bạn thay/viết thành:
```sh
edit 9e1d264 commit trước ba
pick 4b6e19a commit trước hai
pick f4037ec commit trước
```
Lệnh này bảo rebase là bạn muốn thay đổi commit trước ba và giữ hai commit kia không thay đổi. Sau đó bạn save (và đóng) trình soạn thảo. Git bây giờ sẽ bắt đầu rebase. Nó dừng lại ở commit bạn để là edit và cho bạn cơ hội thay đổi commit đấy. Bây giờ bạn có thể cho thêm những thay đổi bạn lỡ không cho vào lần đầu. Để làm thế, bạn edit rồi stage những thay đổi đấyNow you can apply the changes which you missed applying when you initially committed that commit. Sau đó bạn chạy lệnh:
```sh
(your-branch)$ git commit --amend
```
Lệnh bảo Git là cần tạo lại commit, nhưng giữ nguyên thông điệp commit. which tells Git to recreate the commit, but to leave the commit message unedited. Thế là xong phần khó nhất. Cuối cùng là chạy lệnh:
```sh
(your-branch)$ git rebase --continue
```
Lệnh trên sẽ giải quyết phần còn lại.
## Staging (sân chuyển tiếp)
### Tôi muốn nâng lên stage tất cả file đang theo dõi và bỏ qua file không theo dõi
```sh
$ git add -u
```
#### Chỉ nâng một phần các file đang theo dõi
```sh
# Để nâng các file dạng .txt
$ git add -u *.txt
# Để nang các file trong thu mục src
$ git add -u src/
```
### Tôi cần cho thêm các thay đổi đang trong stage vào commit trước
```sh
(my-branch*)$ git commit --amend
```
Nếu bạn đã biết bạn không muốn thay đổi thông điệp commit, bạn có thể yêu cầu git sử dụng lại commit message:
```sh
(my-branch*)$ git commit --amend -C HEAD
```
### Tôi muốn stage một phần của một file mới, nhưng không phải toàn bộ file
Thông thường, nếu bạn muốn stage một phần của một file, bạn chạy lệnh này:
```sh
$ git add --patch filename.x
```
Bạn có thể dùng `-p` thay `--patch` cho ngắn. Lệnh này sẽ mở chế độ interactive. Bạn có thể cho thêm `s` để cắt commit - tuy nhiên, nếu là file mới, bạn sẽ không có lựa chọn này. Để thêm một file mới, làm như sau:
```sh
$ git add -N filename.x
```
Sau đó, bạn sẽ cần sử dụng `e` để thủ công thêm dòng. Chạy lệnh `git diff --cached` hoặc
`git diff --staged` sẽ cho bạn thấy những dòng bạn đã stage so với những dòng vẫn lưu ở local.
### Tôi muốn thêm các thay đổi trong một file vào 2 commit khác nhau
`git add` sẽ thêm toàn bộ file vào một commit. `git add -p` sẽ cho vào chế độ tương tác để chọn những thay đổi bạn muốn thêm vào.
### Tôi cho lên stage quá nhiều thay đổi, và tôi muốn tách ra thành các commit khác nhau
`git reset -p` sẽ mở chế độ patch và hộp thoại để reset. Việc này sẽ giống như với lệnh `git add -p`, ngoại trừ là việc chọn "yes" sẽ đưa thay đổi khỏi stage, loại trừ nó khỏi commit tiếp đến.
### Tôi muốn cho lên stage các chỉnh sửa chưa được stage và bỏ khỏi stage các chỉnh sửa đã stage
Phần lớn thời gian, bạn nên hạ tất cả các file đã trên stage và chọn lại những file bạn muốn commit.Nhưng giả sử bạn muốn thay các thay đổi lên và hạ stage, bạn có thể tạo một commit tạm thời, nâng lên stage các thay đổi, rồi stash (cất) nó. Sau đó, reset cái commit tạm thời rồi pop cái stage bạn vừa cất.
```sh
$ git commit -m "WIP"
$ git add . # "." sẽ thêm tất cả file chưa theo dõi
$ git stash
$ git reset HEAD^
$ git stash pop --index 0
```
GHI CHÚ 1: Lý do để dùng `pop` là để giữ nguyên các thay đổi nhất có thể.
GHI CHÚ 2: Các file đã nâng lên stage sẽ bị hạ nếu không có thêm cờ `--index`. ([Link](https://stackoverflow.com/questions/31595873/git-stash-with-staged-files-does-stash-convert-staged-files-to-unstaged?answertab=active#tab-top) explains why.)
### Tôi muốn unstage 1 file đã stage cụ thể
Đôi khi chúng tôi có một hoặc nhiều tệp vô tình bị stage và những tệp này chưa được commit trước đó. Để loại bỏ chúng:
```sh
$ git reset --
```
Lệnh trên dẫn đến việc unstage tệp và làm cho nó không bị track.
## Hủy bỏ những thay đổi (Discarding changes)
### Tôi muốn bỏ các thay đôi chưa trong commit tại local (đã lên hoặc chưa lên stage)
Nếu bạn muốn bỏ tất cả các thay đổi đã lên hoặc chưa lên stage tại local của bạn, bạn có thể làm như sau:
```sh
(my-branch)$ git reset --hard
# hoặc
(main)$ git checkout -f
```
Lệnh sau sẽ hạ khỏi stage tất cả thay đổi bạn đã cho lên stage với `git add`:
```sh
$ git reset
```
Lệnh sau sẽ đảo ngược tất cả các thay đổi chưa commit tại local (nên chạy tại thư mục gốc repo):
```sh
$ git checkout .
```
Bạn cũng có thể đảo ngược các thay đổi chưa commit cho một file hoặc một thư mục cụ thể:
```sh
$ git checkout [thư_mục|file.txt]
```
Tuy nhiên, một cách khác để đảo ngược tất cả các thay đổi chưa commit (dài hơn để nhập, nhưng hoạt động từ bất kỳ thư mục con nào):
```sh
$ git reset --hard HEAD
```
Lệnh trên sẽ xoá tất cả các file chưa được theo dõi(untracked) tại local, do đó, chỉ các file đã được theo dõi bởi git (tracked) còn tồn:
```sh
$ git clean -fd
```
Thêm cờ `-x` để xoá tất cả các file đã ignore.
### Tôi muốn loại bỏ các thay đổi cụ thể chưa lên stage
Khi bạn muốn loại bỏ một số, nhưng không phải tất cả, các thay đổi trong bản sao làm việc của bạn.
Checkout các thay đổi không mong muốn, giữ các thay đổi tốt.
```sh
$ git checkout -p
# Trả lời y đối với những thay đổi bạn không muốn giữ
```
Một cách khác thì sử dụng `stash` (cất). Cất tất cả các thay đổi tốt, reset bản sao làm việc và apply lại các thay đổi tốt.
```sh
$ git stash -p
# Chọn những thay đổi bạn muốn giữ
$ git reset --hard
$ git stash pop
```
Ngoài ra, còn cách cất những thay đổi không mong muốn của bạn và sau đó drop stash.
```sh
$ git stash -p
# Chọn những thay đổi bạn không muốn giữ
$ git stash drop
```
### Tôi muốn loại bỏ các file cụ thể chưa lên stage
Khi bạn muốn loại bỏ một file cụ thể trong bản sao làm việc của bạn.
```sh
$ git checkout FileCủaTôi
```
Ngoài ra, để loại bỏ nhiều file trong bản sao làm việc của bạn, hãy liệt kê tất cả chúng.
```sh
$ git checkout FileThứNhất FileThứHai
```
### Tôi muốn chỉ loại bỏ các thay đổi chưa lên stage tại local
Khi bạn muốn loại bỏ tất cả các thay đổi chưa commit mà chưa stage tại local
```sh
$ git checkout .
```
### Tôi muốn loại bỏ tất cả các file chưa được theo dõi (track)
Khi bạn muốn loại bỏ tất cả các file chưa được theo dõi
```sh
$ git clean -f
```
## Nhánh
### Tôi muốn liệt kê tất cả các nhánh
Liệt kê các nhanh tại local
```sh
$ git branch
```
Liệt kê cách nhánh trên remote
```sh
$ git branch -r
```
Liệt kê tất cả các nhánh (cả local và remote)
```sh
$ git branch -a
```
### Tạo một nhánh mới từ một commit
```sh
$ git checkout -b
```
### Tôi đã pull (kéo) từ/vào sai nhánh
Đây là một cơ hội khác để dùng `git reflog` để xem HEAD đã trỏ ở đâu trước khi pull sai.
```sh
(main)$ git reflog
ab7555f HEAD@{0}: pull origin nhánh-sai: Fast-forward
c5bc55a HEAD@{1}: checkout: checkout message goes here
```
Chỉ cần reset nhánh của bạn về commit mong muốn:
```sh
$ git reset --hard c5bc55a
```
Xong.
### Tôi muốn loại bỏ các commit tại local để nhánh của tôi giống như nhánh trên server
Kiểm tra rằng bạn chưa push các thay đổi của mình đến server.
`git status` sẽ hiển thị số lượng các commit bạn có hơn origin:
```sh
(my-branch)$ git status
# On branch my-branch
# Your branch is ahead of 'origin/my-branch' by 2 commits.
# (use "git push" to publish your local commits)
#
```
Một cách để reset về origin (để có nhánh giống như trên remote) là chạy lệnh:
```sh
(my-branch)$ git reset --hard origin/my-branch
```
### Tôi muốn chuyển những thay đổi chưa lên stage tới nhánh mới
```sh
$ git checkout -b my-branch
```
### Tôi muốn chuyển những thay đổi chưa lên stage tới nhánh khác đã tạo
```sh
$ git stash
$ git checkout my-branch
$ git stash pop
```
### Tôi đã tạo commit lên main thay vì một nhánh mới
Tạo nhánh mới trong khi giữ main:
```sh
(main)$ git branch my-branch
```
Reset nhánh main đến commit trước đó:
```sh
(main)$ git reset --hard HEAD^
```
`HEAD^` là viết tắt của `HEAD^1`. Đây là viết tắt của parent thứ nhất `HEAD`, tương tự `HEAD^2` là viết tắt của parent thứ hai của commit (merge có thể có 2 parent).
Chú ý rằng `HEAD^2` **không** giống như `HEAD~2` (xem [link](http://www.paulboxley.com/blog/2011/06/git-caret-and-tilde) để thêm thông tin).
Ngoài ra, nếu bạn không muốn sử dụng `HEAD^`, tìm mã hash của commit mà bạn muốn main trỏ về(`git log` sẽ giúp bạn). Sau đó reset về mã hash đấy. `git push` sẽ đảm bảo rằng thay đổi này sẽ hiện trên remote của bạn.
Ví dụ, nếu hash của commit mà nhánh main của bạn đáng ra là `a13b85e`:
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
Checkout một nhánh mới để tiếp tục làm việc:
```sh
(main)$ git checkout my-branch
```
### Tôi muốn giữ toàn bộ file từ một ref-ish khác
Giả sử bạn có một cột mũi làm việc (xem Ghi Chú), với hàng trăm thay đổi. Mọi thứ đang hoạt động. Bây giờ, bạn commit vào một nhánh khác để lưu những thay đổi đó:
```sh
(solution)$ git add -A && git commit -m "Cho tất cả các thay đổi trong cột mũi làm việc này vào một commit to."
```
Khi bạn muốn đặt nó vào một nhánh (có thể là feature, có thể `develop`), bạn quan tâm đến việc giữ toàn bộ các file. Bạn muốn chia commit lớn của bạn thành những cái nhỏ hơn.
Giả sử bạn có:
* nhánh `solution`, với giáp pháp bạn phát triển với cột mũi làm việc của bạn. Hơn `develop` một commit.
* nhánh `develop`, nơi bạn muốn thêm các thay đổi của bạn.
Bạn có thể giải quyết bằng cách mang nội dung thay đổi sang nhánh của bạn:
```sh
(develop)$ git checkout solution -- file1.txt
```
Lệnh trên sẽ lấy nội dung của tập tin đó trong nhánh `solution` đến nhánh `develop` của bạn:
```sh
# On branch develop
# Your branch is up-to-date with 'origin/develop'.
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
# modified: file1.txt
```
Sau đó, commit như bình thường.
Lưu ý: Cột mũi giải pháp được phát triển để phân tích hoặc giải quyết vấn đề. Các giải pháp này được sử dụng để ước tính và loại bỏ sau khi mọi người hiểu rõ vấn đề. ~ [Wikipedia](https://en.wikipedia.org/wiki/Extreme_programming_practices).
### Tôi đã thực hiện một số commit trên một nhánh mặc dù chúng nên ở các nhánh khác nhau
Giả sử bạn đang ở trên nhánh main của bạn. Chạy `git log`, bạn thấy bạn đã thực hiện 2 commit:
```sh
(main)$ git log
commit e3851e817c451cc36f2e6f3049db528415e3c114
Author: Alex Lee
Date: Tue Jul 22 15:39:27 2014 -0400
Bug #21 - Added CSRF protection
commit 5ea51731d150f7ddc4a365437931cd8be3bf3131
Author: Alex Lee
Date: Tue Jul 22 15:39:12 2014 -0400
Bug #14 - Fixed spacing on title
commit a13b85e984171c6e2a1729bb061994525f626d14
Author: Aki Rose
Date: Tue Jul 21 01:12:48 2014 -0400
First commit
```
Hãy lưu ý các hash commit của chúng ta cho mỗi bug (lỗi) (`e3851e8` cho #21, `5ea5173` cho #14).
Trước tiên, hãy đặt lại nhánh main của chúng ta về commit chính xác (`a13b85e`):
```sh
(main)$ git reset --hard a13b85e
HEAD is now at a13b85e
```
Bây giờ, chúng ta có thể tạo ra một nhánh mới cho lỗi #21 của chúng ta:
```sh
(main)$ git checkout -b 21
(21)$
```
Bây giờ, hãy *cherry-pick* commit cho bug #21 trên đầu của nhánh. Nói tóm lại là chúng ta sẽ áp commit đó, và chỉ commit đó, trực tiếp vào đầu của nhánh.
```sh
(21)$ git cherry-pick e3851e8
```
Tại thời điểm này, có khả năng có thể có xung đột hợp (merge conflicts). Hãy xem phần [**Có một vài xung đột**](#merge-conflict) trong [phần interactive rebasing ở trên](#interactive-rebase) để làm thế nào giải quyết xung đột hợp.
Bây giờ chúng ta hãy tạo một nhánh mới cho bug # 14, cũng dựa trên nhánh main:
```sh
(21)$ git checkout main
(main)$ git checkout -b 14
(14)$
```
Và cuối cùng, hãy cherry-pick commit cho bug #14:
```sh
(14)$ git cherry-pick 5ea5173
```
### Tôi muốn xóa các nhánh local đã bị xóa tại luồng trước (upstream)
Khi bạn kết hợp (merge) một pull request trên GitHub, nó sẽ cho bạn lựa chọn để xóa nhánh đã được kết hợp trong fork của bạn. Nếu bạn không có kế hoạch tiếp tục làm việc trên nhánh đấy, mọi thứ sẽ sạch hơn nếu xóa các bản sao local của nhánh, do đó bạn không tồn đọng một cách lộn xộn tại bản sao làm việc của bạn với các nhánh cũ.
```sh
$ git fetch -p upstream
```
upstream` là remote bạn muốn fetch (gọi) về.
### Tôi vô tình xóa nhánh của tôi
Nếu bạn thường xuyên push lên remote, bạn sẽ an toàn phần lớn thời gian. Nhưng đôi khi bạn có thể sẽ xóa các nhánh của bạn. Giả sử chúng ta tạo một nhánh và tạo một tệp mới:
```sh
(main)$ git checkout -b my-branch
(my-branch)$ git branch
(my-branch)$ touch foo.txt
(my-branch)$ ls
README.md foo.txt
```
Hãy thêm nó và rồi tạo commit.
```sh
(my-branch)$ git add .
(my-branch)$ git commit -m 'foo.txt added'
(my-branch)$ foo.txt added
1 files changed, 1 insertions(+)
create mode 100644 foo.txt
(my-branch)$ git log
commit 4e3cd85a670ced7cc17a2b5d8d3d809ac88d5012
Author: siemiatj
Date: Wed Jul 30 00:34:10 2014 +0200
foo.txt added
commit 69204cdf0acbab201619d95ad8295928e7f411d5
Author: Kate Hudson
Date: Tue Jul 29 13:14:46 2014 -0400
Fixes #6: Force pushing after amending commits
```
Bây giờ chúng ta chuyển lại về main và 'vô tình' xóa nhánh của chúng ta
```sh
(my-branch)$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
(main)$ git branch -D my-branch
Deleted branch my-branch (was 4e3cd85).
(main)$ echo ôi không,tôi delete nhánh tôi!
ôi không,tôi delete nhánh tôi!
```
Tại thời điểm này, bạn nên làm quen với 'reflog', một logger (ký sử) được nâng cấp. Nó lưu trữ lịch sử của tất cả các hành động trong repo.
```
(main)$ git reflog
69204cd HEAD@{0}: checkout: moving from my-branch to main
4e3cd85 HEAD@{1}: commit: foo.txt added
69204cd HEAD@{2}: checkout: moving from main to my-branch
```
Như bạn có thể thấy chúng ta có số hash của commit từ nhánh đã xóa của chúng ta. Hãy xem liệu chúng ta có thể khôi phục nhánh đã xóa của chúng ta hay không.
```sh
(main)$ git checkout -b my-branch-help
Switched to a new branch 'my-branch-help'
(my-branch-help)$ git reset --hard 4e3cd85
HEAD is now at 4e3cd85 foo.txt added
(my-branch-help)$ ls
README.md foo.txt
```
Và đấy! Chúng ta đã phục hồi lại được file bị xóa của chúng ta. `git reflog` cũng hữu ích khi rebase tạo sai lầm lớn.
### Tôi muốn xoá một nhánh
Để xoá một nhánh tại remote:
```sh
(main)$ git push origin --delete my-branch
```
Bạn cũng có thể chạy :
```sh
(main)$ git push origin :my-branch
```
Để xoá nhánh tại local:
```sh
(main)$ git branch -d my-branch
```
Để xoá một nhánh local *chưa được* merge với nhánh hiện tại hoặc trên upstream (luồng trước):
```sh
(main)$ git branch -D my-branch
```
### Tôi muốn xoá nhiều nhánh
Giả sử bạn muốn xoá tất cả các nhánh bắt đầu với `fix/`:
```sh
(main)$ git branch | grep 'fix/' | xargs git branch -d
```
### Tôi muốn đổi tên một nhánh
Để đổi tên nhánh local hiện tại:
```sh
(main)$ git branch -m tên-mới
```
Để đổi tên nhánh local khác:
```sh
(main)$ git branch -m tên-cũ tên-mới
```
Để vừa xóa nhánh `tên-cũ` tại remote và push nhánh `tên-mới` từ local:
```sh
(main)$ git push origin :tên_cũ tên_mới
```
### Tôi muốn checkout đến một nhánh remote mà người khác đang làm việc trên đó
Đầu tiên, fetch tất cả nhánh từ remote:
```sh
(main)$ git fetch --all
```
Giả sử bạn muốn checkout sang `daves` từ remote.
```sh
(main)$ git checkout --track origin/daves
Branch daves set up to track remote branch daves from origin.
Switched to a new branch 'daves'
```
(`--track` là viết tắt của `git checkout -b [branch] [remotename]/[branch]`)
Lệnh này sẽ cung cấp cho bạn một bản sao tại local của nhánh `daves` và mọi cập nhật đã được push cũng sẽ được hiển thị từ remote.
### Tôi muốn tạo một nhánh remote mới từ một nhánh local hiện tại
```sh
$ git push HEAD
```
Nếu bạn cũng muốn đặt nhánh remote là upstream cho nhánh hiện tại, sử dụng:
```sh
$ git push -u HEAD
```
Với chế độ `upstream` và `simple` (mặc định trong Git 2.0) của cấu hình `push.default`, lệnh sau sẽ push nhánh hiện tại lên nhánh remote được đăng ký trước đó với `-u`:
```sh
$ git push
```
Các hành vi của các chế độ khác của `git push` được mô tả trong [doc cho `push.default`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-pushdefault).
### Tôi muốn thiết lập một nhánh remote làm upstream (luồng trước) cho một nhánh local
Bạn có thể thiết lập một nhánh remote làm upstream cho nhánh local hiện tại bằng cách chạy lệnh:
```sh
$ git branch --set-upstream-to [remotename]/[branch]
# hoặc, dùng ký tắt:
$ git branch -u [remotename]/[branch]
```
Để thiết lập nhánh upstream remote cho nhánh local khác:
```sh
$ git branch -u [remotename]/[branch] [local-branch]
```
### Tôi muốn để HEAD của tôi dõi theo nhánh mặc định của remote
Bằng cách kiểm tra các nhánh remote của bạn, bạn có thể thấy nhánh remote nào mà HEAD của bạn đang theo dõi. Trong một số trường hợp, có thể đấy không phải là nhánh mong muốn.
```sh
$ git branch -r
origin/HEAD -> origin/gh-pages
origin/main
```
Để thay đổi `origin/HEAD` sang theo dõi `origin/main`, bạn có thể chạy lệnh này:
```sh
$ git remote set-head origin --auto
origin/HEAD set to main
```
### Tôi đã thực hiện thay đổi trên sai nhánh
Bạn đã thực hiện các thay đổi chưa được commit và nhận ra bạn đang ở sai nhánh. Stash (cất) các thay đổi và apply (áp dụng) chúng vào nhánh bạn muốn:
```sh
(wrong_branch)$ git stash
(wrong_branch)$ git checkout nhánh_đúng
(correct_branch)$ git stash apply
```
### Tôi muốn tách một nhánh thành hai
Bạn đã tạo rất nhiều commit trên một nhành và bây giờ bạn muốn tách nhánh ra thành hai, một nhánh kết thúc với một commit cũ, và một nhánh với tất cả các thay đổi.
Dùng `git log` để tìm commit bạn muốn làm mốc để tách. Sau đó chạy lệnh như sau:
```sh
(original_branch)$ git checkout -b new_branch
(new_branch)$ git checkout original_branch
(original_branch)$ git reset --hard
```
Nếu bạn trước đó đã push nhánh gốc lên remote, bạn sẽ cần phải push ép (force push). Để thêm thông tin xem [Stack Overlflow](https://stackoverflow.com/questions/28983458/how-to-split-a-branch-in-two-with-git/28983843#28983843).
## Rebasing và Merging
### Tôi muốn hoàn tác rebase/merge
Bạn có thể đã merge hoặc rebase nhánh hiện tại của bạn với một nhánh sai hoặc bạn không thể tìm ra cách hoàn thành quá trình rebase/merge. Git lưu con trỏ original HEAD trong một variable (biến) được gọi là ORIG_HEAD trước khi chạy các hành động nguy hiểm, vì vậy bạn có thể dễ dàng khôi phục lại trạng thái trước khi rebase/merge.
```sh
(my-branch)$ git reset --hard ORIG_HEAD
```
### Tôi đã rebase, nhưng tôi không muốn push ép (force push)
Thật không may, bạn bắt buộc phải push ép, nếu bạn muốn những thay đổi đó được phản ánh trên nhánh remote. Điều này là do bạn đã thay đổi lịch sử. Nhánh remote sẽ không chấp nhận thay đổi trừ khi bạn push ép. Đây là một trong những lý do chính khiến nhiều người sử dụng quy trình làm việc trên merge, thay vì quy trình làm việc trên rebasing - các nhóm lớn có thể gặp rắc rối khi developer push ép. Nên sử dụng rebase một cách thận trọng. Một cách an toàn hơn để sử dụng rebase không là không phản ánh các thay đổi của bạn trên nhánh remote và thay vào đó thực hiện các thao tác sau:
```sh
(main)$ git checkout my-branch
(my-branch)$ git rebase -i main
(my-branch)$ git checkout main
(main)$ git merge --ff-only my-branch
```
Để biết thêm hãy xem [chủ đề này trên SO](https://stackoverflow.com/questions/11058312/how-can-i-use-git-rebase-without-requiring-a-forced-push).
### Tôi cần kết hợp các commit
Giả sử bạn đang làm việc trong một nhánh có / sẽ trở thành một pull-request cho `main`. Trong trường hợp đơn giản nhất khi bạn chỉ muốn là kết hợp *tất cả* các commit thành một commit và bạn không quan tâm đến timestamp (mốc thời gian) của commit, bạn có thể reset và commit lại. Đảm bảo rằng nhánh main được cập nhật và tất cả các thay đổi của bạn được commit, sau đó:
```sh
(my-branch)$ git reset --soft main
(my-branch)$ git commit -am "New awesome feature"
```
Nếu bạn muốn kiểm soát được nhiều hơn và cũng để bảo vệ timestamp, bạn cần phải làm một vài thứ được gọi là interactive rebase:
```sh
(my-branch)$ git rebase -i main
```
Nếu bạn không làm việc với một nhánh khác, bạn phải rebase tương đối so với `HEAD` của bạn. Nếu bạn muốn gộp 2 commit cuối, bạn sẽ phải rebase tới `HEAD~2`. Cho 3 commit cuối, `HEAD~3`,...
```sh
(main)$ git rebase -i HEAD~2
```
Sau khi bạn chạy lệnh interactive rebase, bạn sẽ thấy trông giống thế này trong trình soạn thảo (text editor) của bạn:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
pick b729ad5 fixup
pick e3851e8 another fix
# Rebase 8074d12..b729ad5 onto 8074d12
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```
Tất cả các dòng bắt đầu bằng một `#` là các comment (chú thích), chúng sẽ không ảnh hưởng đến rebase của bạn.
Sau đó bạn thay thể lệnh `pick` với những bất cứ lệnh nào trong danh sách trên và bạn cũng có thể loại bỏ các commit khỏi rebase bằng cách xoá các dòng tương ứng.
Ví dụ, nếu bạn muốnn **dữ nguyên commit cũ nhất(đầu tiên) và kết hợp tất cả commit sau với commit cũ thứ hai**, bạn nên chỉnh sửa chữ cái bên cạnh mỗi commit ngoại trừ chữ cái đầu tiên và chữ cái thứ hai với `f`:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
f b729ad5 fixup
f e3851e8 another fix
```
Nếu bạn muốn kết hợp tất cả các commit **và đổi tên commit**, bạn nên thêm một chữ cái `r` bên cạnh commit thứ 2 hoặc đơn giản sử dụng `s` thay vì `f`:
```vim
pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
s b729ad5 fixup
s e3851e8 another fix
```
Bạn có thể đổi tên commit trong đoạn hội thoại sẽ bật lên.
```vim
Newer, awesomer features
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# rebase in progress; onto 8074d12
# You are currently editing a commit while rebasing branch 'main' on '8074d12'.
#
# Changes to be committed:
# modified: README.md
#
```
Nếu mọi thứ thành công, bạn sẽ thấy giống như thế này:
```sh
(main)$ Successfully rebased and updated refs/heads/main.
```
#### Chiến lược merge an toàn
`--no-commit` thực hiện merge nhưng giả vờ kết hợp không thành công và không tự động tạo commit, cho phép người dùng có cơ hội kiểm tra và chỉnh thêm kết quả merge trước khi commit. `no-ff` duy trì bằng chứng rằng một nhánh tính năng đã từng tồn tại, giữ lịch sử dự án nhất quán.
```sh
(main)$ git merge --no-ff --no-commit my-branch
```
#### Tôi cần merge một nhánh thành một commit duy nhất
```sh
(main)$ git merge --squash my-branch
```
#### Tôi chỉ muốn kết hợp các commit chưa push
Đôi khi bạn có một số commit trong khi công-việc-đang-tiến-hành và bạn muốn kết hợp thành một trước khi bạn đẩy lên upstream. Bạn không muốn vô tình kết hợp bất kỳ commit nào đã được push lên upstream vì người khác có thể đã thực hiện các commit tham chiếu chúng.
```sh
(main)$ git rebase -i @{u}
```
Lệnh này sẽ thực hành một interactive rebase mà chỉ liệt kê các commit bạn chưa push, vì vậy mọi thứ sẽ an toàn để sắp xếp lại / sửa chữa / squash (gộp) bất cứ gì trong danh sách
#### Tôi cần huỷ bỏ merge
Đôi khi việc merge có thể gây ra sự cố trong một số file nhất định, trong những trường hợp đó, chúng ta có thể sử dụng cờ `abort` để hủy bỏ quá trình giải quyết xung đột hiện tại và cố gắng xây dựng lại trạng thái trước merge.
```sh
(my-branch)$ git merge --abort
```
Lệnh này có sẵn từ phiên bản Git >= 1.7.4
### Tôi cần cập nhật commit gốc (parent commit) cho nhánh của tôi
Giả sử tôi có một nhánh main, một nhánh feature-1 tách từ main và một nhánh feature-2 tách từ feature-1. Nếu tôi thực hiện commit đối với feature-1, thì commit của feature-2 không còn chính xác nữa (gốc nên là đầu của feature-1, vì chúng ta đã tách nhánh từ nó). Chúng ta có thể sửa vấn đề này với `git rebase --onto`.
```sh
(feature-2)$ git rebase --onto feature-1 feature-2
```
Lệnh này giúp trong các trường hợp khó nơi bạn có thể có một feature được xây dựng trên một feature khác chưa được merge, hoặc một bugfix (vá lỗi) trên nhánh feature-1 cần được phản ánh trong nhánh feature-2 của bạn.
### Kiểm tra xem tất cả commit trên một nhánh đã được merge
Để kiểm tra tất cả commit trên một nhánh đã được merge vào nhánh khác, bạn nên diff (khác biệt) giữa các head (hoặc các commit) của các nhánh:
```sh
(main)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
```
Lệnh này sẽ cho bạn biết nếu bất kỳ commit ở trong một nhánh nhưng không trong nhánh kia, và sẽ cung cấp cho bạn một danh sách của bất kỳ tệp không chia sẽ giữa các nhánh. Một lựa chọn khác là chạy lệnh:
```sh
(main)$ git log main ^feature/120-on-scroll --no-merges
```
### Các vấn đề có thể xảy ra với interactive rebase
#### Màn hình chỉnh sửa rebase ghi 'noop'
Nếu bạn thấy như sau:
```
noop
```
Điều này có nghĩa bạn đang cố rebase lại một nhánh đang có commit giống hệt hoặc là *ở trước* nhánh hiện tại. Bạn có thể thử:
* đảm bảo nhánh main của bạn ở đúng chỗ
* rebase với `HEAD~2` hoặc cũ hơn
#### Có một vài xung đột
Nếu bạn không thể hoàn tất thành công rebase, bạn có thể phải giải quyết xung đột.
Đầu tiên chạy `git status` để xem tệp nào có xung đột:
```sh
(my-branch)$ git status
On branch my-branch
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
both modified: README.md
```
Trong ví dụ đó, `README.md` có xung đột. Mở tệp đó và tìm những dòng trông như sau:
```vim
<<<<<<< HEAD
some code
=========
some code
>>>>>>> new-commit
```
Bạn sẽ cần phải giải quyết sự khác biệt giữa code đã được thêm vào với commit mới của bạn (trong ví dụ, mọi thứ từ dòng ở giữa cho đến `new-commit`) và `HEAD` của bạn.
Nếu bạn muốn giữ phiên bản code của một nhánh, bạn có thể sử dụng `--ours` hoặc `--theirs`:
```sh
(main*)$ git checkout --ours README.md
```
- Khi *đang merge*, sử dụng `--ours` để giữ các thay đổi từ nhánh local, hoặc `--theirs` để giữ các thay đổi từ nhánh khác.
- Khi *đang rebase*, sử dụng `--theirs` để giữ các thay đổi từ nhánh local, hoặc `--ours` để giữ các thay đổi từ nhánh khác. Để hiểu giải thích về sự hoán đổi này, hãy xem [ghi chú này trong tài liệu Git](https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---merge).
Nếu việc merge phức tạp hơn, bạn có thể sử dụng trình chỉnh sửa khác biệt trực quan (visual diff editor):
```sh
(main*)$ git mergetool -t opendiff
```
Sau khi bạn đã giải quyết tất cả xung đột và đã kiểm tra code của mình, `git add` các file đã thay đổi và sau đó tiếp tục rebase với `git rebase --continue`
```sh
(my-branch)$ git add README.md
(my-branch)$ git rebase --continue
```
Nếu sau khi giải quyết tất cả các xung đột bạn kết thúc với một cây giống hệt với cái trước khi thực hiện, bạn cần `git rebase --skip`.
Nếu bất kỳ lúc nào bạn muốn dừng toàn bộ quá trình rebase và quay trở lại trạng thái ban đầu nhánh của bạn, bạn có thể làm như thế này:
```sh
(my-branch)$ git rebase --abort
```
## Stash (Cất)
### Stash tất cả chỉnh sửa
Để stash tất cả các chỉnh sửa trong thư mục làm việc
```sh
$ git stash
```
Nếu bạn cũng muốn stash các file chưa được theo dõi, sử dụng cờ `-u`.
```sh
$ git stash -u
```
### Stash các file cụ thể
Để stash chỉ một file từ thư mục làm việc
```sh
$ git stash push working-directory-path/filename.ext
```
Để stash nhiều file từ thư mục làm việc
```sh
$ git stash push working-directory-path/filename1.ext working-directory-path/filename2.ext
```
### Stash với message (thông điệp)
```sh
$ git stash save
```
hoặc
```sh
$ git stash push -m
```
### Apply một stash cụ thể từ danh sách
Đầu tiên kiểm tra danh sách các stash với message bằng lệnh
```sh
$ git stash list
```
Sau đó apply (áp dụng) một stash cụ thể từ danh sách với
```sh
$ git stash apply "stash@{n}"
```
Ở đây, 'n' cho biết vị trí của stash trong stack. Stash trên cùng sẽ là vị trí 0.
Hơn nữa, cũng có thể chỉ stash dựa vào mốc thời gian
```sh
$ git stash apply "stash@{2.hours.ago}"
```
### Stash trong khi giữ các thay đổi chưa stage
Bạn có thể tạo một `stash commit`, rồi dùng lệnh `git stash store`.
```sh
$ git stash create
$ git stash store -m CREATED_SHA1
```
## Finding (Tìm)
### Tôi muốn tìm một chuỗi ký tự trong bất kỳ commit nào
Để tìm một chuỗi ký tự được giới thiệu với commit, bạn có thể sử dụng lệnh như sau:
```sh
$ git log -S "chuỗi ký tự để tìm"
```
Các cờ thường dùng:
* `--source` có nghĩa là hiển thị tên ref được đưa ra trên dòng lệnh mà mỗi lần commit đã đạt tới.
* `--all` nghĩa là bắt đầu từ mọi nhánh.
* `--reverse` in theo thứ tự ngược lại, có nghĩa là hiển thị commit đầu tiên đã thực hiện thay đổi.
### Tôi muốn tìm tác giả hoặc người commit
Để tìm tất cả commit từ tác giả hoặc người commit bạn có thể sử dụng:
```sh
$ git log --author=
$ git log --committer=
```
Hãy nhớ rằng tác giả và người commit không giống nhau. `--author` là người ban đầu viết code; mặt khác, `--committer`, là người đã commit code thay mặt tác giả gốc.
### Tôi muốn liệt kê các commit chứa các file cụ thể
Để tìm tất cả các commit chưa một file cụ thể bạn có thể sử dụng:
```sh
$ git log --
```
Bạn thường sẽ chỉ định một đường dẫn (filepath) chính xác, nhưng bạn cũng có thể sử dụng các ký tự đại diện bất kỳ cho đường dẫn và tên tệp:
```sh
$ git log -- **/*.js
```
Trong khi sử dụng ký tự đại diện bất kỳ, sẽ hữu ích hơn khi thêm `--name-status` để xem danh sách các tệp trong commit:
```sh
$ git log --name-status -- **/*.js
```
### Tôi muốn xem lịch sử commit của một function (chức năng) cụ thể
Để truy tìm lịch sử tiến hóa của một function là dùng lệnh:
```sh
$ git log -L :TênFunction:FilePath
```
Ghi chú là bạn có thể xây dựng lệnh trên thêm với các cờ `git log` khác, giống như [phạm vi sửa đổi](https://git-scm.com/docs/gitrevisions) và [hạn mức commit](https://git-scm.com/docs/git-log/#_commit_limiting).
### Tìm một tag mà một commit đã tham chiếu
Để tìm tất cả các tag có chứa một commit cụ thể
```sh
$ git tag --contains
```
## Submodules
### Clone tất cả submodules
```sh
$ git clone --recursive git://github.com/foo/bar.git
```
Nếu đã clone:
```sh
$ git submodule update --init --recursive
```
### Xoá một submodule
Tạo một submodule là khá rõ rành, nhưng xóa chúng ít không như vậy. Các lệnh bạn cần là:
```sh
$ git submodule deinit submodulename
$ git rm submodulename
$ git rm --cached submodulename
$ rm -rf .git/modules/submodulename
```
## Miscellaneous Objects (Những thứ khác)
### Copy thư mục hoặc tệp file từ một nhánh sang nhánh khác
```sh
$ git checkout -- |
```
### Khôi phục một file đã bị xoá
Đầu tiên tìm commit cuối cùng ma file vẫn tồn tại:
```sh
$ git rev-list -n 1 HEAD -- filename
```
Sau đó checkout file:
```
git checkout id-của-commit-delete-trên^ -- filename
```
### Xoá tag
```sh
$ git tag -d
$ git push :refs/tags/
```
### Khôi phục một tag đã bị xoá
Nếu bạn muốn khôi phục tag đã bị xóa, bạn có thể làm được vậy với các bước sau: Trước tiên, bạn cần phải tìm tag không thể truy cập
```sh
$ git fsck --unreachable | grep tag
```
Ghi lại mã hash của tag. Sau đó, khôi phục tag đã xóa theo cách sử dụng [`git update-ref`](https://git-scm.com/docs/git-update-ref):
```sh
$ git update-ref refs/tags/
```
Tag của bạn bây giờ đã được khôi phục.
### Patch (Vá) bị xóa
Nếu ai đó đã gửi cho bạn một pull request trên GitHub, nhưng sau đó đã xoá chúng trên fork gốc, bạn sẽ không thể clone repository của họ hoặc sử dụng `git am` vì url của [.diff, .patch](https://github.com/blog/967-github-secrets) không dùng được. Nhưng bạn có thể checkout chính PR bằng cách sử dụng [GitHub's special refs](https://gist.github.com/piscisaureus/3342247). Để fetch nội dung của PR#1 vào một nhánh được gọi là pr_1, chạy:
```sh
$ git fetch origin refs/pull/1/head:pr_1
From github.com:foo/bar
* [new ref] refs/pull/1/head -> pr_1
```
### Xuất một repository ra một file Zip
```sh
$ git archive --format zip --output /full/path/to/zipfile.zip main
```
### Push một nhánh và một tag có tên giống nhau
Nếu có một tag trên một remote repository mà có tên giống với một nhánh bạn sẽ gặp phải lỗi khi cố push nhanh với một lệnh `$ git push ` bình thường.
```sh
$ git push origin
error: dst refspec same matches more than one.
error: failed to push some refs to ''
```
Sửa lỗi này bằng cách chỉ định bạn muốn đẩy tham chiếu của head.
```sh
$ git push origin refs/heads/ |