45 GIT classic operation scenarios, specializing in code

Posted by imranlink on Fri, 04 Mar 2022 17:52:34 +0100

Hello, I'm Xiaofu~

Technology exchange pays attention to official account: something inside programmers

Portal: Original address

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 will not 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 want to reset my hard content once

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 display which rows are temporarily stored 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 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 where git reflog is used to find the direction of HEAD before this error 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 whole file complete. You want a large submission separated into smaller ones.

Suppose you have:

  • Branch solution, with prototype scheme, is ahead of the branch of develop ment.
  • Branch develop ment, where you can 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 bring 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 with the 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 let's #21 create a new branch of bug:

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

Next, we use cherry pick to put the submission of the bug #21 into the current branch. 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, execute cherry pick for the bug #14:

(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

I want to undo rebase/merge

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 single 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 but does not automatically submit, giving users the opportunity to check and modify before submitting. 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 conflicts. 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 you resolve all conflicts and tests, git add the changed file, and then use git rebase --continue to rebase.

(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

Temporarily save all changes in your working directory

$ 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 branch, 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.

Technology exchange pays attention to official account: something inside programmers

Portal: Original address