Git is difficult.... I thought I could do it by making a coping cheat sheet, but no luck....
Whenever there is a problem, we don't know what to do.
There are multiple ways to write the same thing across commands, but this can be confusing because the original purpose of the command is not clear.
So, I would like to make a note of how to use Git in such a way that you can get through the rest by logic if you remember only the basic commands, even if it requires a few more steps, just as you can multiply by addition without having to learn ninety-nine.
That I was wrong, but it was important.
I was mistaken on the following important points, and that was the bottleneck I was hooked on. Based on my reflection, I will focus on those mistaken points.
- Index is a snapshot
- Commitments have not disappeared.
- Branches can be moved anywhere.
- Conflicts are marked.
Basic Git workflow
Starting from the state of one commit, the flow of the next commit is as follows
- First, we're at the latest commitments.
- Edit some files
- Register the edited file in "Index" with
add <file>
- Leave the commit as the one registered in "Index" with
commit
.
By repeating this sequence, updates are recorded as a commit history.
Index is a snapshot
I had mistakenly thought that only files that had been edited would be registered in the Index, based solely on the above flow.
In reality, however, the Index is a snapshot of the entire repository, and add <file>
will change the corresponding file in the Index snapshot to the edited file.
Then, when you commit, the snapshot of the Index is registered as a new commit.
In other words, both the Index and the commit are snapshots of the entire repository, with the Index acting as an assembly plant for the next commit.
The three constituent members of Git
Git consists of the following three states of operation.
- HEAD" indicating the commit that is currently originating
- Working" indicating the current directory
- Index" indicating the status of the appointment to be committed to register
- Commit" is shown in the figure above the toilet bowl, but refers to the same thing as "HEAD" because it becomes the next "HEAD" when it is committed
- As the name implies, "Working" is the state of the directory in which you are currently working.
Snapshot, not difference
All states are represented as snapshots, not diffs.
(It's in the Git docs, but I didn't quite understand what that meant...)
Example
Let's look at the three state changes using the following as an example.
- Suppose HEAD is in commit "commit_01" and there are three files registered in "commit_01": "test_01.txt", "test_02.txt" and "test_03.txt
- Suppose you edit "test_01.txt" to become "test_01.txt(v1)" and commit it to create "commit_02
start
HEAD, Working, and Index are all in the same state.
When a branch is moved with checkout <branch>
, the files in the directory are also rewritten to the new branch, but at that very moment "Working is rewritten to the HEAD state" is happening.
And although it is not visible from the table, the Index is similarly rewritten to the HEAD status.
(As mentioned above, I mistakenly thought that the Index was empty at first, with only updated files registered...)
file modification
I edited the files in the directory at hand, so only Working has been updated.
git add
Add the updated Working file to the Index with add <file>
.
The Index is a snapshot of the entire file, so for example, if the unedited file "test_02.txt" were to be add test_02.txt
, the state of the file would remain the same and the Index would not change as a result.
commit
When you commit, a new commit is created in the Index snapshot.
The HEAD reference is then switched to that new commit.
This is how Git works, starting from one commit state until the next commit.
attention (heed)
Even if you commit, Working is,Not rewritten to the same state as the new HEAD" Note that this is different from checkout <branch>
.
So, if "test_01.txt(v1)" is edited again to "test_01.txt(v2)" after updating the Index and before committing, and if you commit in that state, the Working will remain edited, as shown below.
Git forward summary
Once we have organized the operations up to this point.
add
- The
add <path>
command applies the status of a specified path in Working to the Index. add
not only adds files, but also removes them from the Index if the Working file has been deleted.Paths can be specified not only directly in files, but also recursively in directories, "*", ". or recursively by specifying with
- You can use
add *
to set Working and Index to the same state
- You can use
rm
- The
rm <path>
command deletes a file from Working and also from the Index. To remove from Index only, use
rm --cached <path>
- Used to keep files in Working, but exclude them from the repository (Untracked)
- Remove from Working only means
/bin/rm <path>
in the shell, so no Git commands
commit
- The
commit
command registers the Index as a commit and moves the HEAD to the registered commit.
So, in the forward direction from file edit to commit, the following three commands are the ones you need to learn without substitutions.
add <path>
rm --cached <path>
commit
git rm <path>
can be substituted for bin/rm <path>
+git add <path>
or bin/rm <path>
+git rm --cached <path>
.
Commitments have not disappeared.
I had a fear of Git, that if I did a bad commit or merge, I might not be able to undo it.
In the default configuration, the commit log shows only the branch you are currently on.
In fact, I wonder if that kind setup of only showing the commitments of the current branch is one of the reasons for the opposite fear.
E.g.: I can't see the big picture.
For example, the default log display range when in branch "b1" is as follows
git log --oneline --graph
* 9852770 (HEAD -> b1) . * b3da73b . * cd58e8e .
If all branches are to be displayed, the following will be shown.
git log --oneline --graph --all
* 9852770 (HEAD -> b1) . * b3da73b . | * d818e15 (b2) . | * 0ed10e8 . |/ * cd58e8e .
If you can see the big picture and know where you are now, you will feel less anxious!
Example: Commit disappears
There is a command called rebase <branch>
that brings the branch you are on behind the branch you specify.
In the previous example, the result of bringing branch "b1" behind branch "b2" is shown below.
git branch b2
* a1791c1 (HEAD -> b1) . * 9266f2a . * d818e15 (b2) . * 0ed10e8 . * cd58e8e .
Branch "b1" is moved behind branch "b2".
At the same time, the commit of the path where branch "b1" wasIt has disappeared!
However, this is just the default setting that makes it invisible and can be seen by adding the "--reflog" option.
git log --oneline --graph --all --reflog
* a1791c1 (HEAD -> b1) . * 9266f2a . * d818e15 (b2) . * 0ed10e8 . | * 9852770 . | * b3da73b . |/ * cd58e8e .
So, if you accidentally rebase
and want to revert to the original commit, you don't have to panic that the original commit has been lost, just move branch "b1" back to the original commit.
As described below, reset --hard 9852770
can be used to move the current branch "b1" to the original commit, and the result of the move is as follows.
git log --oneline --graph --all --reflog
* a1791c1 . * 9266f2a . * d818e15 (b2) . * 0ed10e8 . | * 9852770 (HEAD -> b1) . | * b3da73b . |/ * cd58e8e .
It is safely back to normal!
Interestingly, the commits "9266f2a" and "a1791c1" created when rebase
was done also remain.
Actually,Git has all the commits left!
So you can revert to any commit, so as long as you know how to move branches, you can commit without panic or fear.
Points to note
Git removes commits that do not belong to any branch as unwanted commits. For example, the remaining commits "9266f2a" and "a1791c1" in the previous example.
However, the default retention period for commits is 90 days, so there is no need to worry about them being deleted immediately.
branch
What is brunch?
- A branch is a reference (pointer) to the location of a commit
- One commit location per branch
- HEAD is also a special branch that indicates the commit you are on
checkout
- checkout is a command that moves HEAD to a specified branch commit
- checkout moves HEAD to the specified branch commit and rewrites "Working" and "Index" to be the same as HEAD
- Note that the behavior is different when the destination is specified in a branch and when the destination is specified in a commit.
When the destination is specified as a branch
- Move HEAD to the specified branch
- Rewrite "Working" and "Index" to the same content as HEAD
- HEAD and branches are tied together
- When committed, not only HEAD,And brunch.Move to new commit
When the destination is specified by commit
- Move HEAD to the specified commit
- Rewrite "Working" and "Index" to the same content as HEAD
- HEAD and branch will be untethered.
- When you commit,Only the HEADMove to new commit
Example: If you move to branch "b2" and commit something there
Before the move
* 9852770 (HEAD -> b1) . * b3da73b . | * d818e15 (b2) . | * 0ed10e8 . |/ * cd58e8e .
After moving
git checkout b2
* 9852770 (b1) . * b3da73b . | * d818e15 (HEAD -> b2) . | * 0ed10e8 . |/ * cd58e8e .
HEAD -> b2" and HEAD is tied to the branch.
post-commit
* 971d696 (HEAD -> b2) . * d818e15 . * 0ed10e8 . | * 9852770 (b1) . | * b3da73b . |/ * cd58e8e .
Since HEAD is tied to branch "b2", when you commit, both HEAD and b2 are moved to a new commit.
Example: If you go to commit and commit something there
Before the move
* 9852770 (HEAD -> b1) . * b3da73b . | * d818e15 (b2) . | * 0ed10e8 . |/ * cd58e8e .
After moving
git checkout d818e15
* 9852770 (b1) . * b3da73b . | * d818e15 (HEAD, b2) . | * 0ed10e8 . |/ * cd58e8e .
HEAD is in the same position as "b2", but "HEAD, b2" and HEAD is not in the branchUntethered and independently in place。
post-commit
* f6525a1 (HEAD) . * d818e15 (b2) . * 0ed10e8 . | * 9852770 (b1) . | * b3da73b . |/ * cd58e8e .
Since the HEAD is not tied to a branch, the "b2" position remains the same after commit, and only the HEAD is moved to the new commit position.
reset
Like checkout
, reset
moves HEAD to a specified branch commit, but its behavior differs significantly from that of checkout
.
Basics of reset
- reset is a command that moves HEAD to the specified branch/commit
- Unlike checkout, if HEAD is associated with a branch before the move, the associated branch is also moved to the destination
- If HEAD is not associated with a branch, only HEAD will be moved to the destination
Behavior by reset option
reset has options "--soft", "(none=default)", and "--hard", all of which are available.Super ImportantIt is.
option
"---soft"
- Only HEAD is moved, "Working" and "Index" are not rewritten.
None (default)"
- HEAD moves and "Index" is rewritten in HEAD
"---hard"
- HEAD moves and "Working" and "Index" are rewritten in HEAD
reset
is similar to checkout
, but
reset
is a branchMove it.checkout
is a branchchange
This is the main difference between the two.
When a path is specified in the RESET and CHECKOUT
Both reset
and checkout
allow passing a path as an argument, but the behavior is different from when no path is passed, so it is important to keep this in mind.
If a path is specified with reset
The reset
option allows paths to be specified as arguments only when the option is "(none=default)".
In that case, unlike the case where no path is specified, the HEAD branch is not moved, but only the "Index" of the specified path is rewritten.
--soft" is the same as doing nothing, so there is no command. --hard" has the same behavior as when the path is specified in checkout (see below), so there is no command for this either.
If a path is specified in checkout
When a path is specified with
checkout
, as withreset
, the HEAD is not moved, but only the "Working" and "Index" of the specified path is rewritten.
The reset
and checkout
paths are used when you want to bring in files from other branches or commits.
checkout notes
checkout
cannot be moved because an error occurs if the contents of "Working" and "Index" are different from HEAD.
The fact that the contents of "Working" and "Index" are different from those of HEAD indicates that some changes have been made, so checkout
cannot be executed as a safeguard to prevent changes from being lost due to movement.
Uses of reset
Although reset
is a primitive command, it can be combined to do many things.
However, if you remember the reset
specifications, you do not need to learn them one by one.
Example: Discard the current edit and return to the beginning.
Square Needle
Set "Working" and "Index" to the same as the initial state "HEAD".
git reset --hard HEAD
Example: Undo a commit
Square Needle
Move the branch to one previous commit and set "Working" and "Index" to the same as HEAD.
git reset --hard HEAD^
Example: Summarize commits in progress
Square Needle
Move back to where you want to go back, keeping the current "Index", and commit.
git reset --soft <遡りたいCommit> git commit
summary of RESET and CHECKOUT
The reset
and checkout
are important commands and must be memorized for all behavior with options and paths.
(computer) command | remarks | Rewriting of Working | Index rewriting |
---|---|---|---|
reset --soft <br/cm> |
Branch Move | nashi (Pyrus pyrifolia, esp. var. culta) | nashi (Pyrus pyrifolia, esp. var. culta) |
reset <br/cm> |
Branch Move | nashi (Pyrus pyrifolia, esp. var. culta) | ant |
reset --hard <br/cm> |
Branch Move | ant | ant |
checkout <br/cm> |
Branch Switch | ant | ant |
reset <br/cm> <path> |
No HEAD movement, file only | nashi (Pyrus pyrifolia, esp. var. culta) | ant |
checkout <br/cm> <path> |
No HEAD movement, file only | ant | ant |
Git is not about learning commands for each target task,
It is more like a puzzle where you have to figure out what values you need to set for "HEAD", "Working", "Index", "Branch", and "Commit" in order to achieve the target state, and then you have to combine reset
and checkout
to get the values you need and commit them.
If you use reset
and checkout
,Any branch can be moved to any commit!
All commits, including failed commits, remain, so you can rest assured that you can start over from anywhere if you use it in conjunction with the aforementioned all commits view.
Merge conflicts are marked.
What is MERGE?
The merge
command creates a new merged commit by incorporating the state of another commit into the HEAD commit.
git merge <branch|commit>
merge behavior
- If no conflicts occur when importing, it will import and commit the newly created state.
If a conflict occurs during import, do the following
- Register conflicted files in "Index" as unresolved merge files
- Append the conflicted content to the conflicted file.
- Files that have been updated and do not conflict are registered in the "Index
Response to conflicts
If a merge conflicts, the conflicted file in the "Index" will be marked red with a merge pending marker when viewing the status in status -s
.
git status -s
UU test_01.txt UD test_02.txt R test_00.txt -> test_03.txt A test_05.txt
how to look at something
- From left to right: "Index" and "Working" status
Meaning
- U": merge unresolved
- A": Add file
- D": Delete file
- M": File update
- R": File rename
- ?" : Untracked file (also in red, but not merge outstanding)
To resolve conflicts and commit
As long as there are unresolved merge files (red-letter files) in the "Index", commit cannot be performed.
So, update all the unresolved merge marks with add
, and when all the unresolved merge marks have been cleared, you can commit the merge.
Files that have been successfully merged are already registered in the "Index", so it is OK to add
only files marked as unmerged.
(In the beginning, it was a mystery to me how Git knew how to resolve conflicts, but it was written in the "Index".... That's reasonable.)
If you want to give up on the merge and undo it
When a conflict occurs, HEAD remains at the commit position before the merge, with "Working" and "Index" halfway merged, waiting for the conflict to be resolved.
So, to give up the merge and return to the original state, rewrite "Working" and "Index" to the state before the merge, i.e., the same as HEAD.
git reset --hard HEAD
Remote Repository / Remote Branch
You can use Git not only for personal local use, but also by placing a repository on a server and having multiple people update that repository.
Remote Repository Update Flow
The following is the flow of updating the server content.
- First, copy the server repository locally (or upload the local repository to the server)
- Update and commit locally at will
- Upload local updates to the server
Repeat steps 2 and 3 to update the server.
Remote repository update target
Not all local updates are uploaded to the server.
An arbitrary branch is registered in the remote repository and is called a remote branch, By updating that remote branch, the server repository is updated.
The "remote/branch" view of log
will tell you where the remote repository branch is located in the local repository.
* 51f2962 (HEAD -> dev) . * fca6cc4 (origin/dev) . | * c8bca3d (master) . |/ * ccdf7f8 (origin/master, origin/HEAD) Initial commit
The remote repository has "master" and "dev" branches, and has progressed to the position of "origin/master" and "origin/dev" in the local repository.
Get latest status of remote branch
Remote repositories are updated by others, so after they are brought local, commits may be added and the branch location moved.
So, fetch
will bring the local up to date with the remote.
remote update
LocalOptional Committo update a branch in the remote repository.
However, remote repositories are used by others, so it is very dangerous to easily update from arbitrary commits.
So we usually set up a "tracking branch" and update the remote from that tracking branch to avoid mistakes.
Remote Tracking Branch / Tracking Branch
Remote Tracking Branch
A remote tracking branch is a branch that indicates which local commit the remote branch is now in, and is displayed as "remote/branch".
This is just a marker to show where the remote branch is, so it will not move unless you update it with fetch
.
trace branch
Local branches can be tied to any remote tracking branch.
A local branch that is configured to be tied to a remote tracking branch is called a "tracking branch.
Confirmation Method
The following command will tell you which branch is the remote tracking branch for which
git branch --all -vv
b1 51f2962 . b2 51f2962 . dev 51f2962 [origin/dev: ahead 1] . * master c8bca3d [origin/master: ahead 1] . remotes/origin/HEAD -> origin/master remotes/origin/dev fca6cc4 . remotes/origin/master ccdf7f8 Initial commit
- All branches are displayed
- Remote branches are displayed as "remotes/[remote]/[branch
- Tracking branches are displayed as "[[remote]/[branch]]" to indicate which remote branch they are associated with
Advantages of Using Tracking Branches
- If
push
is done while HEAD is on a tracked branch, the commit ispush
on the remote branch to which that branch points. - If
merge
is done while HEAD is on a tracked branch, the commit on the remote branch to which the branch points will bemerge
.
Note, however, that the branch name must be the same locally and remotely.
If you cannot align branch names, you have no choice.
push <remote> <local_branch>:<remote_branch>
directly with push
.
revert
As described in the following article, "The Really Scary Rebase," once a commit is made on a remote branch, it must not be undone.
So, to undo a commit that has already been uploaded, you can undo the commit indirectly by uploading a commit that cancels it.
The revert
command is used to create a strikeout commit. You can specify the commit you want to strike out.
git revert <commit>
If a conflict occurs, the action to be taken is the same as for merging.
merge revert
If the commit you want to strike was a merge commit, use the "-m" option to specify which commit to keep.
git revert -m <1|2> <commit>
When you use log <commit>
to display the merge commits you want to cancel, two commits are displayed in the "Merge:" field.
commit 68723fcb7803e9b16cc359a29db641cd65b3b1bd
Merge: 96b9a7f cd6e138
If a conflict occurs, the action to be taken is the same as for merging.
merge revert revert
revert only indirectly undoes a commit, it does not really make the past commit It does not really undo past commits.
In other words, even if you revert the merge, the merge is still done, so if you revert the merge and then want to do the reverted merge again, instead of reverting the merge, you can use the "merge revert revert(complicated...). (Complicated...)
Temporary evacuation of work
Sometimes you want to temporarily save the state you are working on and work on something else.
In such cases, use stash
.
temporary evacuation
git stash
then the "Working" and "Index" states will be saved in a different location and the "Working" and "Index" states will be the same as HEAD.
The stash
can be done anywhere, any number of times, and the saves are stacked.
stash list display
A list of all stash
projects conducted so far can be found at the following link.
git stash list
stash@{0}: WIP on b2: 96b9a7f . stash@{1}: WIP on b1: 8a96143 .
stash site confirmation
If you look at the commit log, you will see that the commit extends from where you did stash
, so you can see where you did stash
." You can move branches after doing stash
.
* 7e3e31d (refs/stash) WIP on b2: 96b9a7f . |\ | * 53f5622 index on b2: 96b9a7f . |/ * 96b9a7f (HEAD -> b2) . | * cb4b54f WIP on b1: 8a96143 . | |\ | | * c64b84e index on b1: 8a96143 . | |/ | * 8a96143 (b1) . | * bb5c8e8 . | * cd6e138 . |/ * 51f2962 .
Restore stash
To undo what you have saved, checkout
where you did stash
, and there you will find
git stash apply stash@{x} --index
then the "Working" and "Index" statuses are restored ("x" is the value displayed in stash list
). ("x" is the value displayed in stash list
)
Since stash
itself is only one commit, it can be used as an arbitrary commit.
Delete stash
Stacked stash
can be deleted as follows
git stash drop stash@{x}
Finally.
This is a rough memo on how to use Git.
Finally, I will write a Git cheat sheet for myself.
Git Cheat Sheet
# Index git add <file> # Index Registration git add * # Register all Indexes git rm --cached <file> # Index deletion # commit git commit -m "<comment>" # branch git branch <branch> # making git branch -d <branch> # deletion git branch -D <branch> # Forced deletion # reset・checkout ## No file specified git reset --soft <branch|commit> # Working->X Index->X git reset <branch|commit> # Working->X Index->O git reset --hard <branch|commit> # Working->O Index->O git checkout <branch|commit> # Working->O Index->O ## With file specification git reset <branch|commit> <file> # Working->X Index->O git checkout <branch|commit> <file> # Working->O Index->O # merge git merge <branch|commit> # remote git push <remote> <local_branch>:<remote_branch> # Remote Branch Creation git push <remote> --delete <branch> # Remote Branch Deletion git branch -u <remote/branch> <branch> # Tracking branch ties git branch --unset-upstream <branch> # Tracking Branch Release git fetch # Get latest remote branch (relevant branch) git fetch <remote> # Remote branch latest acquisition (all branches) git push # push (corresponding branch) git push <remote> <local_branch>:<remote_branch> # push (full designation) # libert git revert <commit> # uncommitted git revert -m x <commit> # Merge commit canceled (m confirmed by log <commit>) # confirmation git status -s # Simplified status indication git log --oneline --graph --all --reflog # Full log display git log -p # log diff display git log --stat # commit log file name display git diff # HEAD <-> Working Comparison git diff --cached # HEAD <-> Index comparison git diff <branch|commit> <branch|commit> # Inter-commit Comparison git branch --all -vv # Branch list and confirmation of linkage # temporary evacuation git stash # temporary evacuation git stash list # (display at) a glance git stash apply stash@{x} --index # return git stash drop stash@{x} # deletion # Delete all commits that do not belong to a branch git reflog expire --expire=now --all && git gc --aggressive --prune=now
impressions
I tried to reduce the number of commands to memorize, but it became very long....
After all, you have to learn a lot....
Git is a
- Overhead view of all commits
- Comparison between HEAD and Working/Index
- Rewriting of HEAD, Branch, Working, and Index using reset and checkout
I felt that the key to the success of the project was the
Since commits always remain and cannot be deleted or modified later, it is rather safe to say that there is no possibility of failure and breaking past commits.
I think reset
is the most important command, although it is not often mentioned. I'd rather ask myself, "How can I organize commit traffic without using reset
?
Reference Articles
https://git-scm.com/book/ja/v2 https://gist.github.com/ktx2207/3167fa69531bdd6b44f1 https://qiita.com/misopeso/items/ede49b661cc7ad30528a https://qiita.com/Iyutaka/items/83264e4377bc398c1463