94
头图

Hello everyone, my name is Xiaofu~

Technical exchanges pay attention to the public number: Programmer's internal affairs

Portal: address

git should not be unfamiliar to everyone, skilled use of git has become a basic skill for programmers, although there are excellent client tools such as Sourcetree at work, which makes merging code very convenient. But job interviews and some scenarios that need to show personal strength still require us to master enough git commands.

Below we have sorted out 45 classic operation scenarios for daily use of git code, basically covering the needs of work.

What did I just submit?

If you commit a change with git commit -a and you are not sure what exactly you committed this time. You can display the most recent commit on the current HEAD with the following command:

(main)$ git show

or

$ git log -n1 -p

My commit message is wrong

If your commit message is wrong and the commit has not been pushed yet, you can modify the commit message in the following way:

$ git commit --amend --only

This will open your default editor, where you can edit the information. On the other hand, you can do it all at once with one command:

$ git commit --amend --only -m 'xxxxxxx'

If you have already pushed the commit, you can modify the commit and force push, but this is not recommended.

The username 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 'git filter-branch' guide page.

I want to remove a file from a commit

Remove a file from a commit by doing the following:

$ git checkout HEAD^ myfile
$ git add -A
$ git commit --amend

This can be very useful when you have an open patch and you committed an unnecessary file to it and 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 also mess up the history of those who have already pulled from the repository. 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 to the remote yet, reset Git to the state it was in before your last commit (while saving the staged changes):

(my-branch*)$ git reset --soft HEAD@{1}

This only works before there is no push. If you've already pushed, the only safe thing to do is git revert SHAofBadCommit , which creates a new commit that undoes all changes from the previous commit; or, if you Pushing this branch is rebase-safe (eg: other developers won't pull from this branch), just use git push -f .

delete any commit

Same caveat: don't do this unless you have to.

$ git rebase --onto SHA1_OF_BAD_COMMIT^ SHA1_OF_BAD_COMMIT
$ git push -f [remote] [branch]

Or do a interactive rebase to delete the lines in the commit you want to delete.

I try to push an amended commit (amended commit) to the remote, but I get an error:

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 amending will replace the old with a new commit of , so if you have pushed a pre-amending commit to the remote before, you are now You have to force push ( -f ). Note - is always make sure you specify a branch!

(my-branch)$ git push origin mybranch -f

In general, should avoid . It is better to create and push a new commit than to force a revised commit. The latter would create conflicts in the source history for developers working on that branch or a sub-branch of that branch.

I accidentally did a hard reset and I want to get my content back

If you accidentally do git reset --hard , you can usually get your commits back, because Git keeps a log of everything and keeps it for a few days.

(main)$ git reflog

You will see a list of your past commits, and a reset commit. Choose the SHA of the commit you want to go back to, and reset it again:

(main)$ git reset --hard SHA1234

This is done.

Staging

I need to add the staged content to the last commit

(my-branch*)$ git commit --amend

I want to stage part of a new file, not the whole of this file

Generally, if you want to stage part of a file, you can do this:

$ git add --patch filename.x

-p . This will turn on interactive mode and you will be able to delimit commits with the s option; however, if the file is new, this option will not be available, when adding a new file, do:

$ git add -N filename.x

Then, you need to use the e option to manually select the lines to be added. Executing git diff --cached will show which lines are staged and which lines are only saved locally.

I want to add changes in one file to two commits

git add will add the entire file to a commit. git add -p allows interactive selection of the parts you want to commit.

I want to turn the temporary content into unstaged, and temporarily store the unstaged content

In most cases, you should make all content unstaged, and then select the content you want to commit.
But assuming that's what you want to do, here you can create a temporary commit to hold your staged content, then stage your unstaged content and stash it. Then reset the last commit to change the original temporary content to unstaged, 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 used here simply because I want to keep it as idempotent as possible.
Note 2: If you don't add --index you will mark the staging file as storage.

Unstaged content

I want to move unstaged content to a new branch

$ git checkout -b my-branch

I want to move unstaged content to another existing branch

$ git stash
$ git checkout my-branch
$ git stash pop

I want to discard local uncommitted changes (uncommitted changes)

If you just want to reset some commits between 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 particular file, you can use the filename as an argument:

$ git reset filename

I want to discard some unstaged content

If you want to discard some, but not all, of the working copy.

Checkout what you don't need and keep what you need.

$ git checkout -p
# Answer y to all of the snippets you want to drop

Another way is to use stash , Stash everything you want to keep, reset the working copy, and reapply the keep.

$ git stash -p
# Select all of the snippets you want to save
$ git reset --hard
$ git stash pop

Alternatively, stash the parts you don't need, then stash drop.

$ git stash -p
# Select all of the snippets you don't want to save
$ git stash drop

Branches

I pulled from the wrong branch or to the wrong branch

This is another case of using git reflog to find where HEAD was pointing 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 the branch to your desired commit:

$ git reset --hard c5bc55a

Finish.

I want to throw away the local commits so that my branch is consistent with the remote

Make sure you haven't pushed your content to the remote first.

git status will show you how many commits ahead of the origin:

(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 is:

(main)$ git reset --hard origin/my-branch

I need to commit to a new branch, but the wrong commit went to main

Create a new branch under main, without switching to the new branch, still under main:

(main)$ git branch my-branch

Reset the main branch to the previous commit:

(main)$ git reset --hard HEAD^

HEAD^ is shorthand for HEAD^1 , which you can reset further by specifying HEAD to set.

Or, if you don't want to use HEAD^ , find the hash of the commit you want to reset to ( git log will do), and reset to that hash. Use git push to sync content to remote.

For example, the hash of the commit the main branch wants to reset to is a13b85e :

(main)$ git reset --hard a13b85e
HEAD is now at a13b85e

Checkout the newly created branch to continue working:

(main)$ git checkout my-branch

I want to keep the whole file from another ref-ish

Let's say you're working on a prototyping project (original working spike (see note)), with hundreds of pieces, each of which works just fine. Now, you've committed to a branch and saved 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 into a branch (probably feature , or develop ), your concern is to keep the whole file intact, and you want one big commit separated into smaller ones.

Suppose you have:

  • Branch solution , has a prototype solution, ahead develop branch.
  • Branch develop , where you apply some content of the prototype scheme.

I can fix this by getting the content into your fork:

(develop)$ git checkout solution -- file1.txt

This will bring the contents of this file from branch solution to branch develop :

# 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, commit as normal.

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 made several commits to the same branch, and these commits should be distributed in different branches

Suppose you have a branch main , execute git log , you see that you made two commits:

(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 flag bugs with commit hash ( e3851e8 for #21, 5ea5173 for #14).

First, we reset the main branch to the correct commit ( a13b85e ):

(main)$ git reset --hard a13b85e
HEAD is now at a13b85e

Now, we create a new branch for bug #21:

(main)$ git checkout -b 21
(21)$

Next, we put the commit for bug #21 into the current branch with cherry-pick . This means that we will apply this commit, and just this one commit, directly on HEAD.

(21)$ git cherry-pick e3851e8

At this time, there may be conflicts here, see Interactive rebasing chapter Conflict section to resolve conflicts.

Also, we create a new branch for bug #14, also based on the main branch

(21)$ git checkout main
(main)$ git checkout -b 14
(14)$

Finally, execute cherry-pick for bug #14:

(14)$ git cherry-pick 5ea5173

I want to delete the local branch where the upstream branch was deleted

Once you have merged a pull request on github, you can delete the merged branch in your fork. If you're not going to continue working on this branch, it's much cleaner to delete your local copy of this branch, so you don't end up in a mess of working branches and a bunch of stale branches.

$ git fetch -p

I accidentally deleted my branch

If you regularly push to the remote, it should be safe in most cases, but sometimes it is possible to delete branches that haven't been pushed to the remote yet. 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 the file and do a commit

(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 we switch back to the main (main) branch and 'accidentally' delete the my-branch 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 point you should be reminded of reflog , an upgraded version of the log, which 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 got the deleted files back. 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 checkout a branch from a remote branch that someone else is working on

First, fetch all branches from the remote:

(main)$ git fetch --all

Suppose you want to checkout from the remote daves branch to the local daves

(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 gives you a local copy of the daves branch, and any pushed updates will be visible to the remote.

Rebasing and Merging

I want to undo the rebase/merge

You can merge or rebase the wrong branch, or fail to complete an ongoing rebase/merge. Git saves the original HEAD in a variable called ORIG_HEAD when performing dangerous operations, so it is easy to restore the branch to the state it was in before the rebase/merge.

(my-branch)$ git reset --hard ORIG_HEAD

I've rebase, 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 commit 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 workflows instead of rebasing workflows, and force pushes by developers can get big teams into trouble. Note that a safe way to use rebase is to not reflect your changes to the remote branch, but to do the following:

(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 working branch will do a pull-request for main . Normally you don't care about the commit timestamp, you just want to combine all commits in into a single one, then reset and recommit. Make sure the main branch is up to date and your changes are committed, then:

(my-branch)$ git reset --soft main
(my-branch)$ git commit -am "New awesome feature"

If you want more control, and want to preserve timestamps, you need to do an interactive rebase:

(my-branch)$ git rebase -i main

You will have to rebase against your own HEAD if there is no other branch relative to it. For example: you want to combine the last two commits (commit), you would rebase relative to HEAD~2 , combine the last 3 commits (commit), relative to HEAD~3 , etc.

(main)$ git rebase -i HEAD~2

After you execute the interactive rebase command, you will see something like 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 starting with # are comments and do not affect rebase.

You can then replace pick with any of the commands in the command list above, and you can also delete a commit by deleting the corresponding line.

For example, if you want to keep the oldest commit alone, and combine all the rest into the second , you should edit every commit after the second commit The preceding word is f :

pick a9c8a1d Some refactoring
pick 01b2fd8 New awesome feature
f b729ad5 fixup
f e3851e8 another fix

If you want to combine the commits and rename the commit , you should add a r next to the second commit, or simply replace s with f :

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 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 similar to the following:

(main)$ Successfully rebased and updated refs/heads/main.

Safe merging strategy

--no-commit performs a merge but does not commit automatically, giving the user the opportunity to review and make changes before committing. no-ff will leave evidence for the existence of a feature branch, keeping the project history consistent.

(main)$ git merge --no-ff --no-commit my-branch

I need to merge a branch into a commit

(main)$ git merge --squash my-branch

I just want to combine the unpushed commits

Sometimes you have several work-in-progress commits before pushing data upstream. At this point, you don't want to include pushed combinations, because others may already have commits referencing them.

(main)$ git rebase -i @{u}

This will generate an interactive rebase, which will only list commits that were not pushed, and it is safe to reorder/fix/squash while this list is in progress.

Check if all commits on the branch have been merged

To check if all commits on one branch have been merged into other branches, you should do a diff between the heads (or any commits) of those 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 the other, and commits that are not shared between branches. Another approach could be:

(main)$ git log main ^feature/120-on-scroll --no-merges

Possible problems with interactive rebase

This rebase edit screen shows 'noop'

If you see this:

noop

This means that the branch you rebase is on the same commit as the current branch, or is ahead of the current branch. You can try:

  • Check to make sure the main (main) branch is OK
  • rebase HEAD~2 or earlier

conflict situation

If you cannot successfully complete the rebase, you may have to resolve conflicts.

First execute git status find out which files are in conflict:

(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 has a conflict. Open the file and find something similar to the following:

   <<<<<<< HEAD
   some code
   =========
   some code
   >>>>>>> new-commit

You need to fix the difference between the newly submitted code (in the example, from the middle line == to new-commit ) and HEAD .

Sometimes these merges are very complex and you should use a visual diff editor:

(main*)$ git mergetool -t opendiff

After you've resolved all conflicts and tested, git add the changed files, then continue the rebase with git rebase --continue .

(my-branch)$ git add README.md
(my-branch)$ git rebase --continue

If you get the same result as before commit after resolving all conflicts, you can execute git rebase --skip .

Anytime you want to end the entire rebase process and go back to the state of the branch before the rebase, you can do:

(my-branch)$ git rebase --abort

Stash

Staging all changes

Staging all changes in your working directory

$ git stash

You can use -u to exclude some files

$ git stash -u

Temporarily save the specified file

Suppose you only want to stage a certain file

$ git stash push working-directory-path/filename.ext

Suppose you want to stage multiple files

$ git stash push working-directory-path/filename1.ext working-directory-path/filename2.ext

Log messages while staging

so you can see it at list

$ git stash save <message>

or

$ git stash push -m <message>

use a specified staging

First you can check your stash record

$ git stash list

Then you can apply some stash

$ git stash apply "stash@{n}"

Here, 'n' is the position of stash in the stack, and the topmost stash will be 0

In addition to that, time stamping can be used (if you can remember it).

$ git stash apply "stash@{2.hours.ago}"

Keep unstaged 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 submodules

$ git clone --recursive git://github.com/foo/bar.git

If already cloned:

$ git submodule update --init --recursive

delete tag

$ git tag -d <tag_name>
$ git push <remote> :refs/tags/<tag_name>

restore deleted tags

If you want to restore a deleted tag, you can follow the steps below: First, you need to find the unreachable tag:

$ git fsck --unreachable | grep tag

Write down the hash of this tag (tag), and then use Git's update-ref

$ git update-ref refs/tags/<tag_name> <hash>

At this point your tags 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's better to manually review their commits, copy them to a new local branch, and do the commits.

After submitting, modify the author, see Change author . Then, apply the changes and initiate a new pull request.

Tracking Files

I just want to change the case of a file name without modifying the content

(main)$ git mv --force myfile MyFile

I want to delete a file from Git, but keep the file

(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 at ~/.gitconfig . I've added some shortcut aliases (and some I easily misspelled) 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 username and password for a repository

You may have a repository that requires authorization, in which case you can cache the username and password instead of typing it every time you push/pull, the 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'm doing wrong

You screwed up: you reset something, or you merged the wrong branch, or you can't find your own commit after a push. There are times when you've been doing great, but you want to go back to a certain state before.

This is what git reflog is for, reflog records any changes to the tip of a branch, even if that tip is not referenced by any branch or tag. Basically, every time HEAD changes, a new record is incremented to reflog . Unfortunately, this only works on local branches, and it only tracks actions (eg, doesn't track any changes to an undocumented file).

(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 the main branch to the 2.2 branch, and back again. There, there is also a hard reset to an older commit. The latest action appears at the top with the HEAD@{0} logo.

If it turns out that you accidentally moved back a commit, the reflog will contain the commit you pointed to on main before you accidentally moved back (0254ea7).

$ git reset --hard 0254ea7

Then use git reset to change main back to the previous commit, which provides a safety net in case the history is accidentally changed.

Technical exchanges follow the public number: Programmer's internal affairs

Portal: address


程序员小富
2.7k 声望5.3k 粉丝