Reprint: https://my.oschina.net/u/4455409/blog/5474550
Git should be familiar to everyone. Skillfully using git has become a basic skill of programmers. Although there are cow X client tools such as Sourcetree at work, which makes it convenient to merge code. But job interviews and some scenes that need to show personal strength still need us to master enough git commands.
Below, we have sorted out 45 classic operation scenarios of git code commonly used in daily use, which basically covers the needs in work.
What did I just submit?
If you submit a change with git commit -a , and you're not sure what you submitted this time. You can use the following command to display the latest commit on the current HEAD:
(main)$ git show
perhaps
$ git log -n1 -p
My commit message is wrong
If your commit message is written incorrectly and the commit has not been pushed, you can modify the commit message through the following methods:
$ git commit --amend --only
This will open your default editor, where you can edit information On the other hand, you can do it all at once with one command:
$ git commit --amend --only -m 'xxxxxxx'
If you have pushed the commit, you can modify the commit and force push, but it is not recommended.
The user name and email address in my commit are incorrect
If this is just a single commit, modify it:
$ git commit --amend --author "New Authorname <authoremail@mydomain.com>"
If you need to modify all history, refer to the guide page of 'git filter branch'
I want to remove a file from a commit
Remove a file from a commit by:
$ git checkout HEAD^ myfile $ git add -A $ git commit --amend
This will be very useful. When you have an open patch and you submit an unnecessary file to it, you need to force push to update the remote patch.
I want to delete my last commit
If you need to delete pushed commits, you can use the following method. However, this will irreversibly change your history and confuse the history of those who have been pulled from the warehouse. In short, if you're not sure, don't do it.
$ git reset HEAD^ --hard $ git push -f [remote] [branch]
If you haven't pushed it to the remote, reset the Git to the state before your last submission (save the changes of the temporary storage at the same time):
(my-branch*)$ git reset --soft HEAD@{1}
This can only be useful before there is no push If you have pushed, the only safe thing you can do is "git revert SHAofBadCommit", a new commit will be created to undo all changes of the previous commit; Or, if the branch you push is rebase safe (for example, other developers won't pull from this branch), just use {git push -f.
Delete any commit
The same warning: don't do this until you have to
$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT $ git push -f [remote] [branch]
Or make one Interactive rebase Delete the corresponding lines in the commit you want to delete.
I tried to push a modified commit to the remote, but an error was reported:
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 rebasing (see below) and renaming will replace the old with a new commit, so if you have pushed a pre revision commit to the remote warehouse before, you must force push now (- F). Note – always make sure you point to a branch!
(my-branch)$ git push origin mybranch -f
Generally speaking, we should avoid pushing It is better to create and push a new commit than to force a revised commit. The latter will cause developers working with the branch or sub branches of the branch to conflict in the source history.
I accidentally made a hard reset. I want to retrieve my content
If you accidentally do git reset --hard, you can usually retrieve your commit, because Git keeps a log of everything for a few days.
(main)$ git reflog
You will see a list of your past commits and a reset commit. Select the SHA of the commit you want to return to and reset it again:
(main)$ git reset --hard SHA1234
That's it.
Staging
I need to add the temporary contents to the last commit
(my-branch*)$ git commit --amend
I want to temporarily store part of a new file, not all of it
Generally speaking, if you want to temporarily store part of a file, you can do this:
$ git add --patch filename.x
-p. abbreviation. This will turn on interactive mode, and you will be able to separate the commit with the "s" option; However, if the file is new, you will not have this option. When adding a new file, do this:
$ git add -N filename.x
Then, you need to use the # e # option to manually select the rows to be added. Executing # git diff --cached # will show which rows are temporarily saved and which rows are only saved locally.
I want to add changes in one file to two commits
git add adds the entire file to a submission git add -p , allows you to interactively select the part you want to submit
I want to change the temporarily stored content into non temporarily stored content and temporarily store the non temporarily stored content
In most cases, you should make all the content not temporarily stored, and then select the content you want to commit. But suppose that's what you want to do. Here you can create a temporary commit to save the content you have temporarily stored, and then temporarily store your non temporarily stored content and stash it. Then reset the last commit, change the content originally temporarily stored into non temporarily stored content, and finally stash pop back.
$ git commit -m "WIP" $ git add . $ git stash $ git reset HEAD^ $ git stash pop --index 0
Note 1: pop is only used here because you want to keep idempotent as much as possible. Note 2: if you do not add -- index, you will mark the temporary file as storage.
Unstaged content
I want to move the content that is not temporarily stored to a new branch
$ git checkout -b my-branch
I want to move content that is not staged to another existing branch
$ git stash $ git checkout my-branch $ git stash pop
I want to discard local uncommitted changes
If you just want to reset some commits between the origin and your local, you can:
# one commit (my-branch)$ git reset --hard HEAD^ # two commits (my-branch)$ git reset --hard HEAD^^ # four commits (my-branch)$ git reset --hard HEAD~4 # or (main)$ git checkout -f
To reset a special file, you can use the file name as a parameter:
$ git reset filename
I want to discard some content that is not temporarily stored
If you want to discard part of the working copy, not all.
Check out the unnecessary contents and keep the necessary ones.
$ git checkout -p # Answer y to all of the snippets you want to drop
Another method is to use the {stash. Stash all the contents to be retained, reset the working copy, and reapply the retained parts.
$ git stash -p # Select all of the snippets you want to save $ git reset --hard $ git stash pop
Or, stash the parts you don't need, and then stash drop.
$ git stash -p # Select all of the snippets you don't want to save $ git stash drop
Branches
I pulled content from the wrong branch, or pulled content to the wrong branch
This is another case of using {git reflog} to find the direction of HEAD before this wrong pull.
(main)$ git reflog ab7555f HEAD@{0}: pull origin wrong-branch: Fast-forward c5bc55a HEAD@{1}: checkout: checkout message goes here
Reset branch to your desired commit:
$ git reset --hard c5bc55a
Done.
I want to throw away the local commit so that my branch is consistent with the remote one
Make sure you don't push your content to the remote first.
git status # will show how many submissions you are ahead of the source:
(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 method is:
(main)$ git reset --hard origin/my-branch
I need to commit to a new branch, but I commit to main by mistake
Create a new branch under main without switching to the new branch, but still under Main:
(main)$ git branch my-branch
Reset the main branch to the previous commit:
(main)$ git reset --hard HEAD^
HEAD ^ is short for HEAD^1. You can reset it further by specifying the HEAD to be set.
Or, if you don't want to use HEAD ^, find the commit hash you want to reset to (GIT log can be completed), and then reset to this hash. Use git push # to synchronize content to the remote.
For example, the submitted hash to which the main branch wants to reset is a13b85e:
(main)$ git reset --hard a13b85e HEAD is now at a13b85e
Check out the newly created branch to continue working:
(main)$ git checkout my-branch
I want to keep the whole file from another ref ISH
Suppose you are working on a prototype project (the original is working spike (see note)), with hundreds of contents, each of which works well. Now you have submitted to a branch to save your work:
(solution)$ git add -A && git commit -m "Adding all changes from this spike into one big commit."
When you want to put it in a branch (maybe feature, or development), your concern is to keep the integrity of the whole file. You want a large submission separated into smaller ones.
Suppose you have:
- Branch solution, with prototype scheme, leading the branch of develop ment.
- Branch develop ment, where you apply some content of the prototype scheme.
I can solve this problem by taking the content to your branch:
(develop)$ git checkout solution -- file1.txt
This will take the contents of this file from branch solution to branch develop ment:
# On branch develop # Your branch is up-to-date with 'origin/develop'. # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: file1.txt
Then, submit normally.
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.
I commit several commits to the same branch, and these commits should be distributed in different branches
Suppose you have a main branch that executes git log, and you see that you have submitted twice:
(main)$ git log commit e3851e817c451cc36f2e6f3049db528415e3c114 Author: Alex Lee <alexlee@example.com> Date: Tue Jul 22 15:39:27 2014 -0400 Bug #21 - Added CSRF protection commit 5ea51731d150f7ddc4a365437931cd8be3bf3131 Author: Alex Lee <alexlee@example.com> Date: Tue Jul 22 15:39:12 2014 -0400 Bug #14 - Fixed spacing on title commit a13b85e984171c6e2a1729bb061994525f626d14 Author: Aki Rose <akirose@example.com> Date: Tue Jul 21 01:12:48 2014 -0400 First commit
Let's mark the bug (e3851e8 for #21, 5ea5173 for #14) with the commit hash
First, we reset the main branch to the correct commit (a13b85e):
(main)$ git reset --hard a13b85e HEAD is now at a13b85e
Now let's #21 create a new branch of bug:
(main)$ git checkout -b 21 (21)$
Next, we put the submission of bug #21 into the current branch with #cherry pick. This means that we will apply this commit, only this commit, directly on the HEAD.
(21)$ git cherry-pick e3851e8
At this time, there may be conflict here. See Interactive rebasing chapter Conflict section Conflict resolution
Furthermore, we create a new branch for bug #14, which is also based on the main branch
(21)$ git checkout main (main)$ git checkout -b 14 (14)$
Finally, for the bug #14, execute # cherry pick:
(14)$ git cherry-pick 5ea5173
I want to delete the local branch whose upstream branch has been deleted
Once you merge a pull request on github, you can delete the merged branches in your fork. If you are not ready to continue working in this branch, it will be cleaner to delete the local copy of this branch, so that you will not fall into the confusion of work branches and a pile of old branches.
$ git fetch -p
I accidentally deleted my branch
If you push to the remote on a regular basis, it should be safe in most cases, but sometimes you may delete branches that have not been pushed to the remote. Let's first create a branch and a new file:
(main)$ git checkout -b my-branch (my-branch)$ git branch (my-branch)$ touch foo.txt (my-branch)$ ls README.md foo.txt
Add files and make a submission
(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 <siemiatj@example.com> Date: Wed Jul 30 00:34:10 2014 +0200 foo.txt added commit 69204cdf0acbab201619d95ad8295928e7f411d5 Author: Kate Hudson <katehudson@example.com> Date: Tue Jul 29 13:14:46 2014 -0400 Fixes #6: Force pushing after amending commits
Now let's switch back to the main branch and 'accidentally' delete the my branch
(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 time, you should think of reflog, an upgraded log that stores the history of all actions in the repository (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 a commit hash from the deleted branch. Let's see if we can restore the deleted branch.
(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
Look! We found the deleted file. Git's reflog is also useful when rebasing goes wrong.
I want to delete a branch
Delete a remote branch:
(main)$ git push origin --delete my-branch
You can also:
(main)$ git push origin :my-branch
Delete a local branch:
(main)$ git branch -D my-branch
I want to check out a branch from a remote branch where someone else is working
First, fetch all branches from the remote:
(main)$ git fetch --all
Suppose you want to check out from the remote daves branch to the local daves branch
(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 short for git checkout -b [branch] [remotename]/[branch])
In this way, a local copy of the daves branch is obtained, and any pushed update can be seen remotely
Rebasing and merging
rebase/merge I want to undo
You can merge or rebase a wrong branch, or you can't complete an ongoing rebase/merge. Git will save the original HEAD in a file called Orig when carrying out dangerous operations_ In the variable of HEAD, so it is easy to restore the branch to the state before rebase/merge.
(my-branch)$ git reset --hard ORIG_HEAD
I've rebase d, but I don't want to force push
Unfortunately, if you want to reflect these changes on the remote branch, you have to force push. Because you fast forward the submission and change the Git history, the remote branch will not accept changes unless you force push. This is one of the main reasons why many people use merge workflow instead of rebasing workflow. The force push of developers will make large teams in trouble. It should be noted that a safe way to use rebase is not to reflect your changes on the remote branch, but to do as follows:
(main)$ git checkout my-branch (my-branch)$ git rebase -i main (my-branch)$ git checkout main (main)$ git merge --ff-only my-branch
I need to combine several commits
Suppose your work branch will do a pull request for "main". Generally, you don't care about the time stamp of the commit. You just want to combine all the commit into a separate one, and then reset and recommit. Make sure the main branch is up-to-date and your changes have been submitted, and then:
(my-branch)$ git reset --soft main (my-branch)$ git commit -am "New awesome feature"
If you want more control and want to retain the timestamp, you need to do interactive rebase:
(my-branch)$ git rebase -i main
If there are no other relative branches, you will have to rebase your HEAD. For example, if you want to combine the last two commits, you will rebase relative to HEAD~2, combine the last three commits, relative to HEAD~3, and so on.
(main)$ git rebase -i HEAD~2
After you execute the interactive rebase command, you will see something similar to the following in your editor:
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 lines beginning with # are comments and will not affect rebase
Then, you can replace {pick with any of the commands in the above command list, or you can delete a commit by deleting the corresponding line.
For example, if you want to keep the oldest commit separately and combine all the rest into the second one, you should edit the word before each commit after the second commit as f:
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 add an r next to the second commit, or simply replace f with s:
pick a9c8a1d Some refactoring pick 01b2fd8 New awesome feature s b729ad5 fixup s e3851e8 another fix
You can rename the commit in the text prompt box that pops up next.
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 successful, you should see something like this:
(main)$ Successfully rebased and updated refs/heads/main.
Secure merging policy
--No commit performs merge without automatic submission, giving users the opportunity to check and modify before submission. No FF will leave evidence for the existence of feature branches and keep the project history consistent.
(main)$ git merge --no-ff --no-commit my-branch
I need to merge one branch into one commit
(main)$ git merge --squash my-branch
I just want to combine the unpushed commit
Sometimes you have several ongoing commitments before pushing data upstream. At this time, we don't want to combine those that have been pushed, because others may have committed to reference them.
(main)$ git rebase -i @{u}
This will produce an interactive rebase, which will only list the submissions that are not pushed. It is safe to reorder / fix / square in this list.
Check whether all commits on the branch have been merged
Check whether all commits on one branch have been merged into other branches. You should make a diff between the heads (or any commits) of these branches:
(main)$ git log --graph --left-right --cherry-pick --oneline HEAD...feature/120-on-scroll
This will tell you a list of all commits that are in one branch but not in another, and those that are not shared between branches. Another approach could be:
(main)$ git log main ^feature/120-on-scroll --no-merges
Possible problems of interactive rebase
'noop' appears on the rebase edit screen
If you see this:
noop
This means that your rebase branch is on the same commit as the current branch, or {ahead of the current branch. You can try:
- Check to make sure there is no problem with the main branch
- rebase # HEAD~2 # or earlier
Conflict situation
If you can't successfully complete rebase, you may have to resolve the conflict.
First, execute "git status" to find out which files have conflicts:
(my-branch)$ git status On branch my-branch Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: README.md
In this example, readme Md# conflict. Open this file and find something similar to the following:
<<<<<<< HEAD some code ========= some code >>>>>>> new-commit
You need to solve the difference between the newly submitted code (in the example, from the middle = = line to the new commit) and the HEAD #
Sometimes these merges are very complex. You should use the visual diff editor:
(main*)$ git mergetool -t opendiff
After all the changes are completed, use the file "add" and "rebase" to resolve the conflicts.
(my-branch)$ git add README.md (my-branch)$ git rebase --continue
If you get the same result after resolving all conflicts as before submission, you can execute git rebase --skip.
Whenever you want to end the whole rebase process and return to the branch state before rebase, you can do the following:
(my-branch)$ git rebase --abort
Stash
Staging all changes
All changes in your working directory are temporarily saved
$ git stash
You can use - u to exclude some files
$ git stash -u
Temporary storage of specified files
Suppose you only want to temporarily store a file
$ git stash push working-directory-path/filename.ext
Suppose you want to temporarily store multiple files
$ git stash push working-directory-path/filename1.ext working-directory-path/filename2.ext
Log messages when staging
So you can see it in the list
$ git stash save <message>
or
$ git stash push -m <message>
Use a specified staging
First, you can check your stash records
$ git stash list
Then you can apply a stash
$ git stash apply "stash@{n}"
Here, 'n' is the position of the stash in the stack, and the top stash will be 0
In addition, you can also use time markers (if you can remember).
$ git stash apply "stash@{2.hours.ago}"
Retain non staged content when staging
You need to manually create a stash commit and then use git stash store.
$ git stash create $ git stash store -m "commit-message" CREATED_SHA1
Miscellaneous objects
Clone all sub modules
$ git clone --recursive git://github.com/foo/bar.git
If you have cloned:
$ git submodule update --init --recursive
Delete tag
$ git tag -d <tag_name> $ git push <remote> :refs/tags/<tag_name>
<a name="recover-tag"></a>
Restore deleted Tags
If you want to restore a deleted tag, you can follow the following steps: first, you need to find the unreachable tag:
$ git fsck --unreachable | grep tag
Write down the hash of this tag, and then use Git's update Ref
$ git update-ref refs/tags/<tag_name> <hash>
At this time, your tag should have been restored.
Patch removed
If someone sends you a pull request on GitHub, but then he deletes his own original fork, you won't be able to clone their commit or use git am. In this case, it is best to manually view their commits, copy them to a new local branch, and then commit.
After submitting, modify the author. See Change author . Then, apply the change and launch a new pull request.
Tracking files
I just want to change the case of a file name without changing the content
(main)$ git mv --force myfile MyFile
I want to delete a file from Git, but keep it
(main)$ git rm --cached log.txt
Configuration
I want to add aliases to some Git commands
Under OS X and Linux, your Git configuration file is stored in ~ / gitconfig. I added some shortcut aliases (and some that I can easily misspell) in the [alias] section, as follows:
[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
I want to cache the user name and password of a repository
You may have a warehouse that needs authorization. At this time, you can cache the user name and password instead of entering it every time you push / pull. Credential helper can help you.
$ git config --global credential.helper cache # Set git to use the credential memory cache
$ git config --global credential.helper 'cache --timeout=3600' # Set the cache to timeout after 1 hour (setting is in seconds)
I don't know what I did wrong
You screwed things up: you} reset something, or you merged the wrong branches, or you pushed and couldn't find your own commit. Sometimes you've been doing well, but you want to go back to a certain state.
This is the purpose of "git reflog". Reflog records any changes to the tip of a branch, even if that top is not referenced by any branch or label. Basically, every time the HEAD changes, a new record will be added to the reflog. Unfortunately, this works only for local branches, and it only tracks actions (for example, it doesn't track any changes to a file that hasn't been recorded).
(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 the check-out from the main branch to the 2.2 branch, and then check back. There is also a hard reset to an older commit. The latest action appears at the top with {HEAD@{0}
If it turns out that you accidentally move back and commit, the reflog will contain the commit (0254ea7) pointed to on the main before you accidentally move back.
$ git reset --hard 0254ea7
Then use git reset to change main back to the previous commit, which provides a safety net in case of accidental changes in history.