Git 语法、使用规范及常见bug总结

分享一下个人总结的git语法、使用规范以及碰到问题的debug(自己感觉能覆盖90%的内容)。

觉得有用的话,帮忙点个赞吧_(手动加狗头)


分支管理常见命令

$ git branch # 查看当前版本库所有分支
$ git branch <name> # 创建新的分支
$ git checkout <name> or git switch <name> # 切换分支
$ git checkout -b <name> or git switch -c <name> # 创建并切换至新的分支
$ git merge <name> # 将name分支合并至当前分支
$ git log --oneline --graph # 查看具体log信息
$ git branch -d <name> # 删除分支
$ git tag <tag_name> # 为当前的commit创建新的标签
$ git tag -d <tag_name> # 删除标签

Login & SSH

Generate SSH Key by ssh-keygen

usage: ssh-keygen [-q] [-a rounds] [-b bits] [-C comment] [-f output_keyfile]
 [-m format] [-N new_passphrase] [-O option]
 [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]
 [-w provider] [-Z cipher]

Debug

Permission Denied

if the following error occur

git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.

Solution

$ ssh-keygen -t rsa -C hongfeiniu1@gmail.com
$ ssh-add ~/.ssh/id_rsa # add ssh key to ssh-agent

Open Connection Failed

Could not open a connection to your authentication agent.

  • Lint the ssh-agent
# linux
$ eval "$(ssh-agent -s)" # option 1
$ eval "ssh-agent" # option 2

# windows
PS D:\code> get-service ssh*
PS D:\code> Set-Service -Name ssh-agent -StartupType Manual
PS D:\code> Start-Service ssh-agent
# 查看ssh-add添加成功
PS D:\code> ssh-add -l
Status   Name               DisplayName
------   ----               -----------
Stopped  ssh-agent          OpenSSH Authentication Agent
  • Copying the Public Key to the Server(未尝试)
$ ssh-copy-id -i ~/.ssh/tatu-key-ecdsa user@host
$ ssh -vT git@github.com # auth the key

Host key verification failed

A通过ssh登陆B时提示 Host key verification failed.

A的known_hosts文件中记录的B的公钥1 与 连接时B传过来的公钥2不匹配。

A通过ssh首次连接到B,B会将公钥1(host key)传递给A,A将公钥1存入known_hosts文件中,以后A再连接B时,B依然会传递给A一个公钥2,OpenSSH会核对公钥,通过对比公钥1与公钥2 是否相同来进行简单的验证,如果公钥不同,OpenSSH会发出警告, 避免你受到DNS Hijack之类的攻击。。

known_hosts字符串格式:ip 公钥

192.168.44.241 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCHtSvSwuK5F32cxU+5zYtZ/EzHPAO5rTvKgvqldLLKp8sM8uB2RivcZT+U+kcl38F73qRK7iGmUUq7ZXMJE0ag=

Solution

  • 方法一:删除A的known_hosts文件中记录的B的公钥(手动进行,不适用于自动化部署情形)

  • 方法二:修改配置文件,在ssh登陆时不通过known_hosts文件进行验证(安全性有所降低),修改完需重启机器

    $ vi ~/.ssh/config #编辑配置文件

    添加以下两行内容:
    StrictHostKeyChecking no
    UserKnownHostsFile /dev/null

    $ ssh-keygen -R 你要访问的IP地址 #update the ssh key? 测试

  • Change the Connection to SSH

$ git remote set-url origin git@github.com:Hongfei-Niu/Note.git
# for Windows
$ git remote set-url origin https://github.com:Hongfei-Niu/Note.git
  • Merge the Branch
$ git config pull.rebase false

Git New Repository Config

Git global setup
git config --global user.name "牛鸿飞"
git config --global user.email "niuhongfei@wuzhi-ai.com"

Create a new repository

git clone git@gitlab.wuzhi-ai.com:niuhongfei/playbook_standardization.git
cd playbook_standardization
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master

Push an existing folder

cd existing_folder
git init
git remote add origin git@gitlab.wuzhi-ai.com:niuhongfei/playbook_standardization.git
git add .
git commit -m "Initial commit"
git push -u origin master

Push an existing Git repository

cd existing_repo
git remote rename origin old-origin
git remote add origin git@gitlab.wuzhi-ai.com:niuhongfei/playbook_standardization.git
git push -u origin --all
git push -u origin --tags

Process the file name and folder

  • modify the filename
git mv old_filename new_filename
  • create new folder
git add folder

Git Commit

追加代码修改内容

如果我们不小心提交了一版我们不满意的代码,并且给它推送到服务器了,在代码没被merge之前我们希望再修改一版满意的,而如果我们不想在服务器上abondon,那么我们怎么做呢?

追加提交,它可以在不增加一个新的commit-id的情况下将新修改的代码追加到前一次的commit-id中

$ git commit --amend

如果master发生变动:

  1. 使用git log来查看版本库中的之前的commit-id
  2. 在终端中执行git reset --hard commit-id,
  3. 运行git log -1 最近的一版就是我们需要的那版,此时再在工作区直接修改代码,改完之后进行git add,再执行本git commit --amend命令,之后git push.

Commit Standard

Format of the commit message: #

<type>(<scope>): <subject>
<body>
<footer>

Message: Subject

First line cannot be longer than 70 characters, second line is always blank and other lines should be wrapped at 80 characters.

Message: Type

Allowed <type> values:

  • feat (new feature)
  • fix (bug fix)
  • docs (changes to documentation)
  • style (formatting, missing semi colons, etc; no code change)
  • refactor (refactoring production code)
  • test (adding missing tests, refactoring tests; no production code change)
  • chore (updating grunt tasks etc; no production code change)

Example: Scope

  • init
  • runner
  • watcher
  • config
  • web-server
  • proxy
  • etc…
    The <scope> can be empty (eg. if the change is a global or difficult to assign to a single component), in which case the parentheses are omitted.

Message: Body

  • uses the imperative, present tense: “change” not “changed” nor “changes”.
  • includes motivation for the change and contrasts with previous behavior.

For more info about message body, see:

  • https://365git.tumblr.com/post/3308646748/writing-git-commit-messages
  • https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html

Message: Footer

Closed issues should be listed on a separate line in the footer prefixed with “Closes” keyword like this:

Closes #234

or in case of multiple issues:

Closes #123, #245, #992

Breaking changes

All breaking changes have to be mentioned in footer with the description of the change, justification and migration notes.

BREAKING CHANGE:

`port-runner` command line option has changed to `runner-port`, so that it is
consistent with the configuration file syntax.

To migrate your project, change all the commands, where you use `--port-runner`
to `--runner-port`.

Commit Example

Capitalized, short (50 chars or less) summary

More detailed explanatory text, if necessary.  Wrap it to about 72
characters or so.  In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body.  The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase can get confused if you run the
two together.

Write your commit message in the imperative: "Fix bug" and not "Fixed bug"
or "Fixes bug."  This convention matches up with commit messages generated
by commands like git merge and git revert.

Further paragraphs come after blank lines.

- Bullet points are okay, too

- Typically a hyphen or asterisk is used for the bullet, followed by a
  single space, with blank lines in between, but conventions vary here

- Use a hanging indent

Git Diff

当前的工作区和上一次commit的版本库

$ git diff

当前的暂存区和上一个commit

$ git diff --cached

当前暂且区和工作区

$ git diff HEAD

不同分支的文件的文件

$ git diff <branchA> <branchB> <filePath>

.gitignore

make gitignore effect

# 注意有个点“.”
git rm -r --cached .
git add -A
git commit -m "chore<.gitignore>: update .gitignore"

.gitignore grammer

补充一下 .gitignore 的匹配规则:

#以此开头的行为注释行
.a #过滤所有 .a 结尾的文件
/a/ #过滤根目录下的 a 文件夹下的所有文件
/a/do.c #过滤指定文件 /a/do.c
!lib.a #从过滤的文件中排除 lib.a
!/a/b #从过滤的文件中排除 根目录下a目录下的b文件
!.c #从过滤的文件中排除所有.c文件
/TODO #仅仅过滤项目根目录下的 TODO 文件,不包括subdir/TODO
build/ #过滤 build/目录下的所有文件

# * 用来匹配零个或多个字符,如*.[oa]忽略所有以".o"或".a"结尾,*~忽略所有以~结尾的文件(这种文件通常被许多编辑器标记为临时文件);
# [] 用来匹配括号内的任一字符,如[abc],也可以在括号内加连接符,如[0-9]匹配0至9的数;
# ? 用来匹配单个字符。
doc/*.txt #过滤doc下所有txt文件,但不包括子目录下的txt文件,如doc/server/arch.txt

doc/**/*.txt # 会忽略掉 doc/ 里面所有的txt文件,包括子目录下的(**/ 从 Git 1.8.2 之后开始支持 **/ 匹配模式,表示递归匹配子目录下的文件)

调试.gitignore文件
有时候,确定为什么要忽略特定文件可能会很困难,尤其是当您使用多个.gitignore文件或复杂格式时。这是git check-ignore命令的用处,告诉git显示匹配模式的详细信息。

例如,要检查为什么忽略www/yarn.lock文件,可以运行:

git check-ignore -v www/yarn.lock
# 输出显示gitignore文件的路径,匹配行的编号和实际模式。
www/.gitignore:31:/yarn.lock  www/yarn.lock

该命令还接受多个文件名作为参数,并且文件不必存在于您的工作树中。

显示所有被忽略的文件
带有–ignored选项的git status命令显示所有被忽略文件的列表:

git status --ignored

Stash 暂存区

当需要拉取远端文件,但又不想提交正在修改的文件时,可以使用git stash 命令将当前的工作区保存(藏匿)起来,等pull 完后再还原。

此功能类似于实现了 ‘局部拉取更新’ 。

命令如下:

git stash //暂存修改工作区
git pull //拉取远端文件
git stash list
git stash pop stash@{0} //将工作区还原

注:如果还原工作区的文件与pull 下来的文件有冲突,要手动修改冲突

git stash pop

  1. 切换分支时,如果当前分支有修改,又不想提交或放弃,可以使用 git stash 将改动存到暂存区。
  2. git stash 后即可以随意切换分支,切换后再使用 git stash pop 即可将暂存区的改动恢复至当前分支。

但如果使用 git stash pop 后有冲突时,如何撤销?

  • 可以使用 git reset --hard ,即可撤销 git stash pop 操作,将当前分支状态恢复。
  • 而该操作后,git stash 暂存区的记录也不会被删除,可通过 git stash show 查看。

Merge Fork

Git remote -v
Git remote add upstream <git link>
Git remote -v
Git fetch upstream
Git checkout master
Git merge upstream/master
Git push origin master

Reset, Rebase & Revert

本地或者自己单独的仓库使用reset 或者revert都可以

涉及到远程仓库(公用仓库)时,不要使用reset,而使用revert回滚

git revert

git revert 撤销某次操作

  • 此次操作之前和之后的commit和history都会保留
  • 并且把这次撤销作为一次最新的提交

git revert [–[no-]edit] [-n] [-m parent-number] [-s] [-S[]] …​

–edit or --no-edit是否弹出commit message窗口

**-n **是 --no-commit的缩写

-m parent-number 存在merge是,指定父系分支号?这个还不怎么懂,暂时略过。

假设master分支上的提交记录如下:

A->B->C->D

目前HEAD指向commit D,我们要将代码revert到B提交时的状态

# Method 1
$ git revert C D # 会生成2个新的commit分别覆盖C、D的提交

# Method 2
$ git revert -n C D # 不会生成新的提交,但是回滚变动会作为修改变动添加到了索引区,可以直接调用git commit保存或者git revert --conitnue弹出commit页面

# Method 3
$ git revert -n C..D
$ git revert C..D # 类似方法1,revert从C到D之间的提交,假设中间还有很多提交时可以用这种

其他操作

$ git revert HEAD # 撤销前一次 commit
$ git revert HEAD^ # 撤销前前一次 commit
$ git revert HEAD~10 # 将head指向上10次的commit。
$ git revert <commit id> #(比如:fa042ce57ebbe5bb9c8db709f719cec2c58ee7ff)撤销指定的版本,撤销也会作为一次提交进行保存。

git revert是提交一个新的版本,将需要revert的版本的内容再反向修改回去,版本会递增,不影响之前提交的内容

git reset

git reset [–soft | --mixed [-N] | --hard | --merge | --keep] [-q] []

参数可以选择:

  • hard :当使用这个命令后,工作区、暂存区的内容都会和当前head指向的内容相同。
  • 直接把 HEAD 指针移动到 commit ID 上,无论修改是否被 add 、 commit ,都直接重置(此时暂存区被清空,工作区的修改被清空),所做的修改将全部丢失,这是非常危险的;
  • 但是如果你修改之后进行了 commit 操作,那非常好办,可以用 reflog 查询相应的 hash 值,进行恢复;
  • git reflog和git log很像,git不会显示已删除的commit 记录(reset操作导致或者其它操作),但是git reflog会显示,然后在重新使用git reset命令;
  • 如果你修改之后,进行了 add 操作,也可以恢复,但是比较麻烦,用 git fsck --lost-found
  • soft: 当使用这个这个命令后,head指针指向指定的commit,但是工作区、暂存区的内容不会发生变化。
  • mixed: 暂存区的内容将会发生变化,但是工作区的内容不会发生变化。

假设master分支上的提交记录如下:

A->B->C->D

目前HEAD指向commit D,我们要将代码回退到B提交时的状态

# 方法1
$ git reset --soft B # 这个时候可以发现,C,D两次提交做的修改依然存在,并且在索引区内,这个时候如果直接调用git commit ,可以生成一个新的提交E,E包含C、D两次提 交的修改。

# 方法2
$ git reset --mixed B # 二者是一样的
$ git reset B # 这个时候可以发现,C,D两次提交做的修改依然存在,但是不在索引区内,记如果需要重新提交,则需要先调用git add。

# 方法3
$ git reset --hard B # 和--soft、--mixed不同的是,C,D两次提交做的修改以及D以后做的一些没有提交的修改都不复存在

--merge说明
这个参数使用的有一定的前提,需要保证没有添加到索引区的修改文件在新旧两个HEAD直接的提交中没有过修改。如果下面命令调用成功

$ git reset --merge B

则会保留没有添加到索引区的修改。即,假设commit C 和 commit D都只修改了a.txt,而在D后我们又修改了b.txt,但是没有调用git add b.txt保存修改到索引区,则调用git reset --merge B 成功后,原来对b.txt做的修改还会存在,但是C、D提交中的修改将会回滚消失

--keep说明
和--merge有一些类似。使用成功的前提是:在D后有本地修改的文件在C、D两次提交中没有修改过,即C、D两次提交中没有它的修改记录。
假设我们在D后修改了a.txt文件,而且C、D两次提交中我们都没有修改a.txt文件,这样我们调用

git reset --keep B 

可以成功,并且a.txt文件中的修改依然会保留。

git rebase

说明:变基操作。

场景 1:本地有多个commit,想合并成一个commit。

假设如下:

master分支提交历史为:A->B->C->D

你后面发现commit C和D有些问题,需要做一些变动,比如

  • 将C、D两次提交合并成一次提交 squash D //将D合并到C提交 或者用fixup D,但是这样会丢弃D的提交message
  • 删除D提交 drop D //删除D
  • 修改D提交的message reword D//修改D提交的message,等等。

则可以调用
git rebase -i B
然后会弹出编辑窗口,相应的做修改即可。也就是变更修改pick、reword、squash、fixup、drop等命令。

pick:保留这份commit。
reword: 保留这份commit,但是会重新编辑commit信息
squash : 这份commit的内容虽然会保留,但是会合并到先前的commit中。
drop: 移除这份commit的内容
等等

场景 2:整合分支

假设origin/master 分支上的提交历史为 A->B->C->D;

  • 你在本地新添加了一个提交E,则master为A->B->C->D->E。
  • 此时队友新推送一个提交N到origin/master,则origin/master 分支上提交历史为A->B->C->D->N。
  • 如果你在本地采用merge的方式,则提交历史会在分叉变成A->B->C-D->(E,N)->M(表示Merge后的新提交)。

而才有rebase方式的话

$ git fetch origin master # 更新代码
$ git rebase origin/master # 变基操作
# 则本地master提交历史可能变成A->B->C->D->N->E不会存在分叉。

场景 3:将某一段commit粘贴到另一个分支上

假设如下:

  • master分支提交历史为:A->B->C
  • server分支提交历史为:A->B->D->E
  • client分支提交历史为:A->B->D->F
$ git rebase [startpoint] [endpoint] --onto [branchName] # 将当前分支的commits 放到目标分支,HEAD anchor还在原先的commit上
$ git checkout <branchName>
$ git reset --hard <new commit id> # 并把HEAD anchor移到最新的commit上

# 新花样
$ git rebase --onto master server client

这样会取出client分支,找出处于client分支和server分支的共同祖先之后的修改,然后把他们在master分支上重演一遍

这样client分支会变成A->B->C->F,注意server和client共同拥有的D提交记录在client里消失了

场景 4: 同步远程分支

git pull --rebase

  • git pull = git fetch && git merge
  • git pull --rebase = git fetch && git rebase

完整流程

$ git rebase --continue 
# 如果有冲突, 解决冲突

# after solving the conflicts
$ git add .
# 不需要commit
$ git rebase --continue
$ git push

Explanation 1
To be specific, it equals to:

$ git fetch origin
$ git rebase --onto origin/foo e(middle commit) foo

Explanation 2
Suppose you have two commits in local branch:

      D---E master
     /
A---B---C---F origin/master

After “git pull”, will be:

      D--------E  
     /          \
A---B---C---F----G   master, origin/master

After “git pull --rebase”, there will be no merge point G. Note that D and E become different commits:

A---B---C---F---D'---E'   master, origin/master

修改分支名称

rename branch

Step 1. 本地分支重命名(还没有推送到远程)

git branch -m oldName newName

Step 2. 远程分支重命名 (已经推送远程-假设本地分支和远程对应分支名称相同)

# a. 重命名远程分支对应的本地分支
git branch -m oldName newName

# b. 删除远程分支
git push --delete origin oldName

# c. 上传新命名的本地分支
git push origin newName

# d.把修改后的本地分支与远程分支关联
git branch --set-upstream-to origin/newName

恢复分支(远程)

Recover the remote branch

思路:找到分支id,本地重新创建该分支,推送到远程

Step 1: 找到分支commitId

很多种方法

git reflog
git log
或者gitlab or github界面Project Activity里找到操作ID

Step 2: 本地重新创建分支,推送到远程

  • git branch recovery_
  • git branch -m recovery_ # 可忽略,保存个备份挺好
  • git push --set-upstream origin recovery_

Tools

gh-md-toz

生成READ.md的目录: gh-md-toc or “Markdown All in One” Plugin in VSCode
gh-md-toc will automatically be available in the PATH

Installation

  • Linux (manual installation)
$ wget https://raw.githubusercontent.com/ekalinin/github-markdown-toc/master/gh-md-toc
$ chmod a+x gh-md-toc
  • MacOS (manual installation)
$ curl https://raw.githubusercontent.com/ekalinin/github-markdown-toc/master/gh-md-toc -o gh-md-toc
$ chmod a+x gh-md-toc
  • Linux or MacOS (using Basher)
$ basher install ekalinin/github-markdown-toc

Example

STDIN

Here’s an example of TOC creating for markdown from STDIN:

$ cat ~/projects/Dockerfile.vim/README.md | ./gh-md-toc -

Local files

Here’s an example of TOC creating for a local README.md:

$ ./gh-md-toc ~/projects/Dockerfile.vim/README.md

Debug

divergent branches and reconcile

hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint: 
hint:   git config pull.rebase false  # merge (the default strategy)
hint:   git config pull.rebase true   # rebase
hint:   git config pull.ff only       # fast-forward only
hint: 
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.

known host problem

When the ip is changed and relevant host get clean, the password would pop up as: "git@github.com’s password: ". May caused by ssh.
研究一下github验证机制

(Git TroubleShooting)[https://docs.github.com/en/authentication/troubleshooting-ssh/error-permission-denied-publickey]

git case insensitive

Git 大小写不敏感,修改目录名大小写失败

$ git config core.ignorecase false

Solution

  1. 文件夹备份
  2. git config core.ignorecase false 设置大小写敏感
  3. cd… 进入到文件夹目录下,我的文件夹名是Container,执行命令删除:git rm Container
  4. 将备份文件夹重命名大小写放回原来路径下
  5. 执行命令添加:git add container
  6. 查看状态:git status
  7. 提交:git commit -m ‘文件夹大小写’
  8. 推到远程分支:git push origin 本地分支名:远程分支名,远程分支若不存在会自动创建 git push origin base_portal_mysql_wanghongfa:base_portal_mysql_wanghongfa

mv to rename

Cause

git mv CyberSecurity/password.md CyberSecurity/ldap.md

Error

fatal: not under version control, source=CyberSecurity/password.md, destination=CyberSecurity/ldap.md
  • Solution1

    Most easiest way to solve this issue is to first we need to add the file first and then we need to rename the file

$ git add file-name
$ git mv current-file-name new-file-name
and then your problem will be solve.
  • Solution2

Caused by Case Sensitive difference between Git and Windows Shell

Another quirk that left me frustrated is if you're using the command line, it will use your current path to git mv the file. So if you have a file in C:\Git\MyRepo\MyFolder\MyFile.txt

If you do:
c:
cd git
cd myrepo
cd myfolder
dir
It will work fine, you'll see your file. But if you type git mv MyFile.txt MyFile2.txt you will get an error because git can't find Myfile.txt.

The reason is that it's looking at your current path, which is c:\git\myrepo\myfolder\

But git is case sensitive. So you have to go back and type
c:
cd Git
cd MyRepo
cd MyFolder
If you then type git mv it will work fine.

Putting this in as an answer for people like me that found this post while debugging this error message.
  • fatal: unable to access The requested URL returned error: 403
edit .git/config

! [remote rejected] master -> master (pre-receive hook declined)

Reference

git push不上去的原因在于所push的分支权限为protected,只有项目的管理员或者项目的管理员指派的具有相应权限的人才能进行push,要进行项目的push,有如下两种方法:

1.将所要push的内容所在的分支的protected权限关闭

  • 进入所在项目的settings
  • 点击进入Protected branches,点击unprotected将master分支的权限改变,即关闭master的protected权限

2.新建其它分支,将项目push到新建的分支上,后期再进行merge

# 新建分支
git branch feature
# 切换分支
git checkout 分支名

# 进行项目上传
git add .
git commit -m "提交的信息"
git remote add origin 远程仓库地址
git push -u origin 分支名

ERROR: Permission to XXX.git denied to user

ERROR: Permission to hbxn740150254/BestoneGitHub.git denied to Chenzuohehe. fatal: Could not read from remote repository.Please make sure you have the correct access rights and the repository exists.

https://www.jianshu.com/p/12badb7e6c10

Github README无法加载图片

域名污染,需要在绕过DNS解析图片链接的域名,在/etc/hosts文件中直接配置ip

xxx.xxx.xxx.xxx raw.githubusercontent.com
xxx.xxx.xxx.xxx githubusercontent.com
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值