Git cli - zhongjiajie/zhongjiajie.github.com GitHub Wiki

Git-cli

  • git追踪的只是修改,不是文件,只有将修改add到stage之后的内容才会被管理

Git相关图片

git-cli相关书籍资料

配置

核心配置

  • 生成公钥私钥: ssh-keygen -t rsa -C "[email protected]"在.ssh文件夹中会生成id_rsa和id_rsa.pub两个文件,其中id_rsa.pub是公钥,可以提供给别人,id_rsa是私钥,要保持仅自己知道
  • 初始化配置:
    • 配置用户名: git config --global user.name "xxx"
    • 配置邮箱: git config --global user.email "[email protected]"
  • 关闭追踪文件权限: git config --global core.fileMode false
  • 启动大小写敏感: git config core.ignorecase false
  • 显示中文文件名: git config --global core.quotepath false
  • 默认的编译器: git config --global core.editor vim
  • 保证window和*unix中的换行符一致: git config --global core.safecrlf true
  • 设置git push的默认方式,对git 2.XGit warning: push.default is unset; its implicit value is changing: git config –global push.default simple
  • 配置github使用git命令访问慢的情况: git config --global http.https://github.com.proxy socks5://127.0.0.1:1086; git config --global https.https://github.com.proxy socks5://127.0.0.1:1086,详见[这里][]

提高效率

  • log更全更好看: git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
  • reflog个性化输出: git config --global alias.rlg "reflog --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative"
  • 开启给 git 输出着色: git config --global color.ui true
  • git config credential.helper store: 保存需要输入账户密码的链接,例如以https的方式链接的,设置了credential.helper store之后只要再输入一次账户密码就会记住了
  • windows中文乱码解决办法: 中文乱码
  • 访问github更加快: git config --global https.proxy 'socks5://127.0.0.1:<PORT>'

config

  • 查看全部配置: git config --list

init

  • git init: 新建一个git版本管理库
  • git --bare init: 新建一个git版本管理库裸库,用于提交commit

clone

将远程库中的代码clone到本地,支持HTTPS协议和SSH协议,建议使用ssh协议,除了传输更快之外,ssh协议还能保存用户账户密码信息,下次pull push可以不用输入

  • git clone: 不会直接在本地生成除了master之外的分支,如需要 clone后 git branch -agit checkout -b dev origin/dev生成本地的dev分支
  • git clone -b mybranch --single-branch git://sub.domain.com/repo.git: Clone only one branch
  • git clone --depth=1 <remote_repo_url>: 只想获取最后一次commit的内容Using git to get just the latest revision
  • git clone https://username:[email protected]/username/repository.git: git clone with username and password通过直接在url中指定用户名和密码,这个有账户密码暴露的风险
  • 使用https/http方式且配置上用户名和密码的clone,遇到特殊符号会报错,因为这种方式是用url的方式链接的,需要对特殊符号进行url编码,编码之后便不会报错,详见Escape @ character in git proxy password

checkout

切换分支;撤销修改

  • git checkout <BRANCH>: 切换到BRANCH分支
  • git checkout -- <FIELNAME>: 撤销FILENAM的修改,(--很重要,如果没有就会变成切换分支),这里撤销修改有两个情况
    • FIELNAME已经添加到暂存区(stage)后,又作了修改,撤销修改就回到添加到暂存区(stage)后的状态
    • FIELNAME一直没有被放到暂存区(stage),撤销修改就回到和版本库状态
  • 撤销修改:
    • git checkout -- .: 撤销当前目录的修改
    • git checkout -- /specific/path: 撤销指定目录的修改

fetch pull

  • Git fetch和git pull区别: fetch 只拉取远程commit数据,改变了remote里面的commit id,不会自动合并;pull拉取最新commit并将其merge,简单的讲git pull = git fetch + git merge
  • fork项目开发,同步代码git fetch upstream; git <merge|rebase> upstream/master; git push origin/master: Git fork 项目后,上游项目更新了 同时更新自己的项目
  • 将origin/upstream的PR远端拉到本地review: git fetch origin/upstream pull/ID/head:BRANCHNAME

branch

  • 列出分支: 本地分支git branch及全部git branch -a
  • 查看分支及对应的远程分支: git branch -vgit branch -vvFind out which remote branch a local branch is tracking
  • 新建分支:
    • 新建分支: git branch <new_branch> <src_branch>从src_branch创建一个新的分支new_branch
    • 新建分支同时切换分支: git checkout -b <new_branch> <src_branch>
  • 删除分支:
    • 删除本地分支: git branch -d <branch>
    • 强行删除没有合并的分支: git branch -D <branch>
    • 删除远程分支: git push oririn :<branch>或者git push --delete origin <brancbranchhName>推荐使用后者(--delete可改成-d)
    • 如果一个分支合并到master后,正确的删除方式是:
      • 远程分支仍存在: git push origin --delete <branch>然后git branch -D <branch>
      • 远程分支合并后在github/gitlab的PR页面已经删除: git fetch origin -p然后git branch -D <branch>
    • 删除本地的remote/<origin|upstream>/branch分支git命令行删除远程分支
      • 如果远程服务器已经没有该分支: git fetch -p或者git pull -p
      • 如果远程服务器还有该分支,想在删除track的分支: git branch --delete --remoets <remote|upstream>/<branch>,或者缩写git branch -dr <remote>/<branch>
  • 远程分支已删除但是本地还有remote/[origin|upstream]/<branch>暂存在本地的远程分支,类似这个: git pull -p或者git fetch -p,-pprune的缩写,所以也可以直接使用git pull/fetch prune

add

将工作区(working tree)中的文件加入到缓存区(staged index)

  • git add <FILENAME>: 指定FILENAME文件加入staged index
  • git add .: 将working tree所有的文件加入staged index
  • git add -f <FILENAME>: 将.gitignore中忽略的FILENAME文件强行加入staged index

commit

  • 允许提交commit信息为空Git commit with no commit message:
    • 仅这次提交为空git commit -a --allow-empty-message -m ''
    • 设置全局别名git config --global alias.nmcommit 'commit -a --allow-empty-message -m ""'
  • 修改上次提交的内容

merge

用于合并分支,如果合并后出现冲突,会以<<<<<<< ======= >>>>>>>标记框出来

  • git merge <BRANCH>: 将BRANCH分支合并到当前分支
  • git merge --ff-only <BRANCH>: 将BRANCH分支仅以fast-forward的方式合并到当前分支
  • git merge --allow-unrelated-histories <BRANCH>: 允许将两个没有相关历史的分支进行合并
  • --squash的使用
    • git checkout master: 切换到master
    • git merge --squash bugfix: merge带上squash标识符,会将多个commit合并成一个commit
    • git commit: 因为合并成一个commit,所以要重新commit一下,此时的commit信息是可编辑的
  • 合并遇到冲突时使用他们的分支内容,详见这里: git merge --strategy-option theirs

rebase

  • git rebase <master|upstream/master>: 当前的bug或者featrue分支完成开发,rebase到master|upstream/master做好向远端提交的准备
  • git rebase -i HEAD^: 发现上一个提交错了,或者review了代码后需要修改,可以rebase上一个commit,会保留之前的修改和commite message.完成后使用git add.; git rebase --continue.但是部分社区不喜欢rebase,喜欢根据review新建一个commit或者revert提交到对branch,从而避免重复review
  • git rebase -i sha-1 modify pick to edit git rebase --continue: How to modify a specified commit in git
  • git rebase --abort: 放弃当前的rebase
  • git rebase --skip: rebase多步解决冲突的过程中,conflict发现和上一个rebase内容一样(即完全舍弃了来自conflict分支的内容),这时使用git rebase --skip进去下一步
  • git rebase -i --root: Change first commit of project with Git? [duplicate]

remote

  • 查看remote: git remote, git remote -v
  • 增加远程仓库: git remote add origin git@server-name:path/repo-name.gitorigin为默认远程仓库名,可以自定义,多个远程仓库类似github上fork了原仓库同时要追踪原仓库的变化时,可以增加一个upstream分支,定期更新该分支并合并origin对应的分支,github Collaborating - Configuring a remote for a fork
  • 删除remote: git remote remove <your_remote_name>
  • 修改remote名称: git remote rename <old> <new>
  • 修改分支对应的地址How to change remote origin from Git: git remote set-url origin git://<new.url.here>
  • 同步upstream内容到origingithub Collaborating - Syncing a fork用于fork开源项目并对其进行二次开发的同步更新
    • 从upstream中更新: git fetch upstream
    • 将master rebase 在upstream/master 上: git checkout master; git rebase upstream/master
    • merge to master: git checkout master; git merge upstream/master,这个操作会将fork/master内容同步成upstream/master的情况,且会将本地的提交放到最git log的最前

log

  • Git 基础 - 查看提交历史:
    • git log: 简单的查看日志命令
    • git show sha-1: 查看指定哈希值的日志
    • git log -n 3: 查看近三条日志记录,可以是本地的,可以是远程的
    • git log -p -2 <filename>: 查看某个文件(可选)的近两次提交
    • git log --stat: 查看日志同时查看每个commit修改了什么内容和改变了多少行
    • git log --graph --oneline --abbrev-commit: 查看git日志,每个commit一行,且显示图,--abbrev-commit显示简短commit哈希值
  • 个性化你的 Git Log 的输出格式 git log --pretty=format:"%h %ci %cn %ce %s" 参数分别是: 哈希 日期 名称 邮箱 概述
  • git shortlog -nes: 查看短log参数为: 短log name + email + summary

reflog

reflog是数据回复工具,在使用Git的过程中,有时会不小心丢失commit信息,这时可以使用reflog进行数据恢复

  • git reflog
  • git reflog --format='%C(auto)%h %<|(17)%gd %C(blue)%ci%C(reset) %s <%an %ae>' 格式化reflog的输出

push

将本地的分支更新的内容和远程分支关联起来,一般适用于需要多人合作的时候

  • git push --set-upstream origin <your_branch_name>或者简写成git -u origin master(推荐): 创建远程分支,将当前分支代码提交到远程分支,并将本地的branch关联到remote
  • git push -f origin: 强力提交代码,会覆盖掉远程代码,例如git回滚到任意版本git reset -hard <commit_id>; git push -f origin(仅作为例子,不推荐使用,更应该使用revert)

diff

diff显示两个版本之间的差异

  • git diff [filename]: 比较工作目录(Working tree)和暂存区(staged index)文件的区别
  • git diff --cached [filename]: 比较暂存区(staged index)和上次提交(HEAD)快照的区别
  • git diff HEAD: 比较工作目录(Working tree)和上次提交(HEAD)
  • git diff HEAD^ HEAD: 查看上次和上上次提交的差别
  • git diff origin/branch-1 [filename]: 比较branch-1本地分支和远端分支的区别
  • git diff <commit_hash1> <commit_hash2>: 比较同一分支上两个commit的diff
  • git diff <commit_hash1> <commit_hash2> <filename>: 比较两个commit某个文件的差别
  • git diff --name-only <commit_hash1> <commit_hash2>: 仅显示两个commit不同文件的名称
  • git diff <branch1>..<branch2>: 比较两个分支上最新的提交的diff
  • git diff --stat <COMMIT> <COMMIT>: diff统计结果,显示每个文件增减的数量,最后显示这个patch的总增减量
  • 查看两个分支的不同:
  • git diff --no-index filename_1 filename_2: 通过git比较linux文件的差异,不用两个文件在git的仓库中
  • git diff --check: 检查git中是否存在结尾的空格,How to not leave trailing whitespace (using your editor or Git),或者使用git的pre-commit钩子

stash

使用场景是:

  • 如果发现在上次提交中忘记了某些内容,并且已经开始在同一分支中处理下一个提交了
  • 在别的分支中工作时,接到非常紧急的需求需要快速完成

stash将在工作目录(Working tree)和暂存区(staged index)的文件暂存起来.stash的存储方式是stack,所以最新的暂存信息会存储在最上面,stash@{<stash_num>}的值越小

  • git stash: 当前分支所有没有 commit 的代码先暂存起来
  • git stash save <stash_name>: 将当前的修改暂存起来 并将暂存重命名为<stash_name>
  • git stash list: 查看当前branch的stash记录
  • git stash apply stash@{N}: 恢复暂存了了stash信息
  • git stash drop: 删除上一条stash
  • git stash drop stash@{N}: 删除指定的git stash
  • git stash pop: 恢复并删除(不建议使用,这个操作不可逆,建议使用git stash apply; git stash drop代替)
  • git stash clear: 清空stash
  • 在从stash恢复前查看stash里面和本地的不同See what's in a stash without applying it: 显示最新stash内容的区别git stash show -p 显示指定stash内容的区别git stash show -p stash@{2}(这个建议在ide中进行查看方便对比和删除stash)
  • git stash apply stash^{/<regex>}: find the stash by name using git's regular expression syntax by stash^{/<regex>} and apply stash How to name and retrieve a stash by name in git

reset

tag

tag是将一个git的commit和标签关联,更容易记住的有意义的名字,能在检查和回退的时候更加便捷.Git使用两种主要类型的标签:

  • 轻量标签(lightweight): 轻量标签很像一个不会改变的分支 - 它只是一个特定提交的引用

  • 附注标签(annotated): 附注标签是存储在 Git 数据库中的一个完整对象。它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;并且可以使用 GNU Privacy Guard (GPG)签名与验证。通常建议创建附注标签,这样你可以拥有以上所有信息;但是如果你只是想用一个临时的标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的

  • 查看所有tag: git tag

  • 为当前commit打上tag: git tag v1.1

  • 为指定commit(一般为过去的)打上tag: git tag v1.1 <commit_hash>

  • 打tag的同时为tag增加说明: git tag -a v1.1 -m "my tag message of tag 1.1"

  • 查看某个tag对应的commit id: git show v1.1

  • 删除本地的tag: git tag -d <tagName>

  • 删除远端的tag: git push --delete origin <tagName>

  • 将本地其中的标签推动到远端: git push origin v1.1.0

  • 将本地所有的tag推送到远端: git push origin --tags

  • 删除类似的tag,如2.0.0-*样式的tag: git tag --list '2.0.0-*' | xargs -I % echo "git tag -d %; git push --delete origin %" | sh

  • checkout到指定标签: 在Git中你并不能真的checkout指定的tag,因为checkout只对branch有效,可以使用git checkout -b <branchname> <tagname>在指定标签上创建一个新分支然后进行checkout

clean

清除某些内容

  • git clean -n: 查看有什么内容会删除但是不会进行实际的删除操作
  • git clean -fdx: 强行删除untracked的文件及文件夹,包括ignore的文件/文件夹
  • git clean -fdxi: 与上面类似但是进入交互性命令行进行删除
-q, --quiet              do not print names of files removed
-n, --dry-run            dry run
-f, --force              force
-i, --interactive        interactive cleaning
-d                       remove whole directories
-e, --exclude <pattern>  add <pattern> to ignore rules
-x                       remove ignored files, too
-X                       remove only ignored files

revert

生成一个新的commit来撤销某次commit的内容.revert和reset的区别

  • git revert HEAD: 撤销上一次commit
  • git revert HEAD^: 撤销上上次commit
  • git revert <commit_hash>: 撤销指定的commit

cherry-pick

Apply the changes introduced by some existing commits

git cherry-pick [--edit] [-n] [-m parent-number] [-s] [-x] [--ff]
        [-S[<keyid>]] <commit>…​
git cherry-pick --continue
git cherry-pick --quit
git cherry-pick --abort
  • -x: 保留原来的commit hash(仅仅在commit信息里面保存),可以使得cherry-pick之后的hash码保持和原来分支中的一样,对于开源项目来说很重要方便检查整体cherry-pick情况,对公共分支来说非常重要,参考

rev-parse

Pick out and massage parameters

  • git rev-parse --short HEAD: 获取HEAD的短hash值
  • git rev-parse --abbrev-ref HEAD: 获取HEAD所属的分支名

rm

删除文件,会同时在本地文件系统和git中删除

  • git rm file: 不再追踪文件,并在文件系统删除文件,删除后还要提交git commit
  • file1git rm --cached file: 不再追踪文件,但是不会在文件系统删除文件,之后git commit

gitignore

.gitignore可以忽略放在git目录里,但是不需要追踪和提交的文件

  • git check-ignore -v <file_name.ext>: 查看gitignore文件中那一行将file_name.ext过滤掉,廖雪峰-git忽略特殊文件.想要将某个文件加入stage index但是一直失败,有可能是gitignore文件配置将其过滤了,导致文件不能被追踪

  • .gitignore可以忽略自己本身改变,不用去编辑.git/info/exclude文件.gitignore gitignore itself

  • A collection of useful .gitignore templates: github中为常用的gitignore文件添加了模板

  • gitignore的写法

    符号 含义
    / 目录 开头表示根目录
    * 多个通配符
    ? 单个通配符
    [] 单个字符匹配列表
    ! 不忽略追踪的文件或目录

ls-files

  • git ls-files -d | xargs echo -e | xargs git checkout --: git恢复所有被删除的文件
  • git ls-files -m | xargs echo -e | xargs git checkout --: git回复所有被修改的文件

update-index

git-update-index: Register file contents in the working tree to the index

根据Do git update-index --assume-unchanged rules propagate to clients on pull知道update-index仅对当前的本地库有效,因为index仅仅是存在本地中

  • git update-index [--skip-worktree | --assume-unchanged] <FILE_NAME> : 将文件保留在git中但是不追踪他的改变.因为在gitignore中的文件将不会在git中,如果要保持文件在git中,但是不追踪文件的改变可以使用update-index.根据Keep file in a Git repo, but don't track changes,应该使用--skip-worktree更好,如果想要恢复改变可以使用--no-skip-worktree/--no-assume-unchanged。一般可以对配项目的配置文件进行设置,防止其被误操作提交到git中
  • git update-index --chmod=+x path/to/file: 修改文件path/to/file变成可执行文件, 想要将文件从可执行变成不可执行使用git update-index --chmod=-x path/to/file

FAQ

git-cli每次要求输入用户名密码

根据这里

  • 使用SSH而非HTTPS: 使用https会导致默认使用账户名密码,git remote -v查看是否使用的协议,通过git remote set-url origin [email protected]:username/repo.git将HTTPS修改成SSH
  • 配置凭证存储机制: git config --global credential.helper store配置凭证存储,仅需下次再输入一次帐号密码就能记住密码

merge和rebase的区别

  • merge: 合并两个git分支时,会按照commit的顺序排序进行合并
  • rebase: 会将当前分支的commit合并到rebase分支HEAD后面,git checkout patch; git rebase master会将master和patch分支合并,且将仅在patch分支中有的commit合到master的HEAD后面

revert和reset的区别

  • revert是用一次新的commit来回滚之前的commit,reset是直接删除指定的commit
  • reset是把HEAD向后移动了一下,而revert是HEAD继续前进,新的commit的内容和要revert的内容正好相反,能够抵消要被revert的内容

github或者gitlab合并PR过来的分支的时候

重命名追踪的文件

  • 一般直接修改文件名就能使得git追踪到重命名
  • 也可以通过cli进行修改,重命名git追踪的文件git mv <folder|file> <folder|file>如果是更改大小写但是平台是不区分大小写的话要引入中间变量git mv src_file tmp; git mv tmp dest_fileIn a Git repository, how to properly rename a directory

怎么写好commit message

获取当前的分支名

  • git branch | grep \* | cut -d ' ' -f2
  • git rev-parse --abbrev-ref HEAD

fork branch clone的区别

so-区别

  • fork: 对存储仓库(repository)进行的远程的、服务器端的拷贝,从源头上就有所区别。复刻实际上不是 Git 的范畴。它更像是个政治/社会概念
  • clone: 是个对某个远程仓库的本地拷贝。克隆时,实际上是拷贝整个源存储仓库,包括所有历史记录和分支
  • branch: 是git的一种机制,处理单一存储仓库中的变更,并最终目的是用于与其他部分代码合并

找回已经删除的分支

详情看这里

  • 先通过reflog找到对应的SHA1值
  • 然后通过git checkout [sha]切换到这个SHA1都应的commit, 再使用git checkout -b [branchname]切换成对应的分支名.或者直接使用git checkout -b <branch> <sha>一步到位将对应的SHA1值切换成对应的分支

合到release或者hotfix分支的合并规则

为了保持原有的规则,一般是使用git checkout release|hotfix; git merge master的方式合并的

调整commit的顺序

Reordering of commits: 使用git rebase -i <HASH>,然后调整顺序,善用ddkP以及ddp命令能有效完成部分操作

git是否保持文件权限

根据Does git store the read, write, execute permissions for files,So git stores only the content in the file and the execute bit.,即仅保持文件的内容和文件是否可执行的状态

如果需要修改已经在git仓库中文件的可执行属性,可以使用update-index中的--chmod=+x或者--chmod=-x

仅获取当前分支名

这里git rev-parse --abbrev-ref HEAD

git提示不在git目录下 但是目录存在

  • HEAD文件的问题,详见这里或者这里。只需要将.git/HEAD文件删除,然后bash运行echo 'ref: refs/heads/master' > .git/HEAD就行了

仅本地忽略部分文但是不共享到别的用户

编辑.git/info/exclude文件,这个文件和.gitignore一样的格式。这个的好处是仅仅在你本地忽略了这部分文件,别人对其是不可感知的,如果这部分文件是已经在git中管理的,则要使用git update-index --assume-unchanged <file-list>让git不要追踪他


⚠️ **GitHub.com Fallback** ⚠️