Skip to content

Instantly share code, notes, and snippets.

@maboloshi
Last active March 10, 2022 00:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save maboloshi/7516252fba4aff0309d50ddf097a4937 to your computer and use it in GitHub Desktop.
Save maboloshi/7516252fba4aff0309d50ddf097a4937 to your computer and use it in GitHub Desktop.
[Git 相关操作]

Git 是 Linus Torvalds 在 2002 年用 C 语言编写的一个分布式版本控制系统


[TOC]

如未说明,尖括号 <> 内的内容表示其并非 git 命令参数,而是用户定义的内容(如具体 仓库名、分支名、文件名等等)。

参考:git-scm git 简明指南 图解 git git 参考手册 archlinux-wiki:git 猴子都能看懂的 git 入门


安装和初始设定

  • 安装 git

    Linux:根据发行版不同,使用安装命令安装git包。

    Mac OS X :安装 Xcode(含xcode command line tools),自带 git,或 brew install git

    Windows :Git for Windows

  • 初始设定

    设置用户名和邮箱,图形界面到其相关设置选项里设置,命令行:

    git config --global user.name "Your Name"
    git config --global user.email "email@example.com"

    更多设置见后文 --git配置

    从本地仓库向远程仓库推送的基本操作流程:

    在本地仓库内进行操作 - > 在本地仓库创建快照版本-> 将本地仓库快 照推送到远程仓库

仓库创建和远程关联

创建本地仓库

  • 创建(/ 初始化)仓库 :git init

  • 仓库空间划分概念:

    • 工作区(working directory ):存放当前工作文件

    • 暂存区(stage):存放通过git add添加的文件。

    • 版本库(repository ):项目的各个版本(快照)。

关联远程仓库

将本仓库与其他仓库关联(例如远程服务器上的仓库),以推送本地仓库数据到关联的仓库中。

  • 添加关联

    可以克隆远程仓库或者直接添加远程仓库信息以进行关联:

    • 克隆仓库:git clone <url/repo-name>

      几种不同的协议示例:

      git clone https://example.com/path/to/repo-nam.git
      git clone git@example.com:someone/path/to/repo-name.git
      git clone ssh://example.com/path/to/repo-name.git
      git clone git://example.com/path/to/repo-name.git

      此外还支持 ftp、git 、 rsync 等协议。

      • 克隆远程仓库的某个分支

        git clone <remote_repo> -b <branch>

        分支相关信息参看分支管理

    • 直接添加远程仓库信息

      git remote add [<option>] <host-name> <url>

      host-name是远程主机名,改名字由用户自定义,如按一般习惯将其命名为origin

  • 删除关联:git remote remove <repo-name>

  • 查看远程仓库信息:git remote

    • 查看远程仓库地址:git remote -v
  • 远程仓库更名:

    git remote rename <oldname> <newname>
  • 修改远程仓库地址(以origin为例):

    git remote set-url origin <new-url>
  • 添加远程仓库地址(以origin为例):

    git remote add-url --add origin <another-url>

    也可以修改仓库目录中.git下的config文件的相关信息进行仓库更名和地址修改。config文件部分内容示例(该origin远程仓库对应三个地址):

    [remote "origin"]
    	url = git@github.com:levinit/itnotes.git
    	url = git@git.coding.net:levinit/itnotes.git
    	url = https://github.com/levinit/itnotes.wiki.git
    	fetch = +refs/heads/*:refs/remotes/origin/*

    提示:向origin推送(push)时将依次推送到三个url地址,但拉取(pull)时仅拉取第一个url地址的仓库内容。

git remote -h可获取更多 remote 相关命令帮助。更多远程仓库相关内容参看下 文推送和获取分支

快照基本操作

Git 保存数据是对文件系统的一组快照。 每次提交更新时,它主要对当时的全部文件制作 一个快照。如果文件没有修改,Git 只保留一个链接指向之前存储的文件。一份快照就是备份的一个文件版本,Git 的工作就是创建和保存项目的快照及与之后的快照进行对比。

一个简单的快照生成流程:

工作区文件 --add--> 进入暂存区 --commit--> 加入版本快照

仓库状态

  • 查看当前仓库状态:git status
    • 简略地显示:git status -s
  • 查看仓库变动内容:git diff
    • 显示变动内容摘要:git diff --stat
      • 查看已暂存的改动:git diff --cached
    • 显示最近快照和工作区内容的差异:git diff HEAD
      • 显示指定文件的差异git diff HEAD --<filename>
  • 查看历史提交:git reflog

提交快照

提交一个快照需要两步操作:

  1. 添加文件到暂存区:git add <file-name>

  2. 提交快照:git commit

每次提交都会生成一个哈希码,即是 commit id。修改过的文件如果不添加到暂存区,就不 会加入到快照中。


  • 所有变动添加到暂存区:git add -A

    变动包括(对文件的)新建、修改和删除,A 是 --all 的缩写,相当于以下两条命令:

    • git add . 将所有**新建和修改(但不包括删除)**提交到暂存区
    • git add -u 将所有**修改和删除(但不包括新建)**提交到暂存区(u--update , 只标记本地有改动的已追踪文件)
    • 撤销暂存区修改:get reset HEAD <file-name>
  • 提交快照并添加注解:git commit -m "about" about 是注解内容

    -m "about"还可和其他操作(如 merge 和 tag 等)合用。

  • 工作区变动直接添加到快照并增加注解:git commit -am "about"

  • 暂存区的变动追加到上一份快照:git commit --amend

    在提交一个快照后又变动了部分内容,但是想把新的变动追加到这个快照中时使用。

    1. 将这些变动添加到暂存区 (git add)

    2. 执行git commit --amend 追加

      该命令会生成的新的 commit id 并替换掉原 commit id; 如果暂存区没有内容 , 可以利用该命令修改上一次提交的注解。

回退快照

  • 回退文件版本

    使用该文件在版本库里的版本替换工作区中的版本,需要先撤销暂存区修改,再撤销工作区修改

    1. 撤销暂存区修改:get reset HEAD <file-name>
    2. 撤销工作区修改:git checkout -- <file-name>
  • 删除快照中的文件

    1. git rm `
    • git commit

    移动或改名版本库中的文件git mv <file-name> <new-file-name>同理,需要进行git commit快照提交才能生效。

    默认情况下git rm <file-name>也会将文件从暂存区和硬盘中(工作目录)删除。如果 要在工作目录中留存该文件,可以使用git rm --cached <file-name>保留。

  • 版本回退——回退到某个指定的版本

    • git reset --hard <commit-id> 回到指定版本并抛弃该版本之后的所有提交
    • git revert --hard <commit-id> 回到指定版本并提交一次

    commit id 可以使用前文的git reflog命令在历史操作记录中查找。

    回退之后要推送到远程仓库使用git push origin HEAD --force

    • 回退到上一个版本:git reset --hard HEAD^

      关于HEAD^~

      • HEAD表示当前分支的当前版本

      • ^( caret)表示父提交,当一个提交有多个父提交时,可以通过在^后面跟一 个数字,该数字表示第几个父提交,HEAD^相当于HEAD^1(第一个父提交) ;

        上一个版本就是HEAD^HEAD^,上两个版本就是HEAD^^

      • (tilde )后跟一个数字 n 相当于前面连续的n 个^HEAD ~ 2就相 当于HEAD^^HEA^1^1

快照信息

git rev-list --count HEAD #最近一次提交快照的版本号(即第几次快照)
git rev-parse HEAD #最近一次提交快照的hash值
git rev-parse --short HEAD #最近一次提交快照的前面部分(7位)hash值

分支管理

分支管理经验示例:

  • master 分支 -- 用于稳定更新,同步到远程仓库;
  • dev 分支 -- 用于开发,同步到远程仓库;
  • bug 分支 -- 用于修复问题,不必同步 ;
  • feature 分支 -- 用于添加新特性,根据情况同步;

……

分支查看

  • 列出所有分支:git branch

    #查看包含指定版本的分支
    git branch --contains <commmit-id>
    #查看本地分支与远程分支对应关系
    git branch -vv
  • 列出当前分支历史记录:git log

    • 查看指定的分支:git log <branch-name>

    • 显示拓扑图:git log -- graph

    • 简洁模式查看:git log --oneline

    • 显示所有的提交信息:git --decorate

    • 特定过滤:

      • 查找有特定内容的注释的分支:git log --grep=<content>

      • 查找特定作者提交的分支:git log --author="name"( name 是作者名字)

      • 按时间范围查看分支:git log --since/befor/until/after={time-discription} 示例:git log --oneline --before={3.weeks.ago} --after={2016-06-06} --no-merges

        --no-merges作用是隐藏合并提交的分支,--oneline是每个提交显示一行

创建、切换、合并和删除分支

  • 创建和切换

    • 创建并切换分支:git checkout -b <branch-name> 也可以先创建分支再切换分支

      • 创建分支:git branch <branch-name>
        • 创建一个空分支:git checkout --orphan <branch-name>
      • 切换分支:git checkout <branch-name>
    • 创建并切换分支,同时关联远程分支:git checkout -b <branch-name> origin/<branch-name>

    • 重命名分支:git branch -m <old-branch-name> <new-branch-name>

      这只是将本地分支重命名,如果要将远程分支也重命名,只需要将本地重命名后的分支推送给原来已经关联的远程分支即可,参看# 推送和下载分支有关推送的说明。

  • 合并

    合并是将指定分支合并到到当前分支git merge <branch-name>

    如果合并分支时存在冲突则需要先解决冲突。

    合并分支时可在命令后面加上-m "info"来添加一个合并说明。

    • 普通方式合并分支:git merge --no-off <branch-name>

      通常合并分支时,如果可能,Git 会用 Fast forward 模式,但这种模式下,删除分支 后,会丢掉分支信息。加--no-off参数,可以用普通模式合并。

    • 合并指定分支到当前分支并丢弃当前分支的历史快照git rebase <branch-name>

      将指定分支(branch-name )合并到当前分支合并后的版本将 “ 嫁接 ” 到 该指定分支上并取代该分支。而当前分支的其余历史快照将会被丢弃,这些历史快 照将会临时保存为补丁 (patch,补丁在.git/rebase目录中 )。

  • 删除分支:git branch -d <branch-name> 强行删除未被合并过的分支:git branch -D <branch-name>

    这只是删除了本地的分支,如果要删除远程仓库的分支,参看下文。

推送和获取分支

  • 分支追踪:设置远程某分支与本地某分支的关联(远程某分支和本地某分支建立起一一对应的关系)
    • git branch --track <branch-name> <origin/branch-name>
    • git branch --set-upstream-to=origin/<branch-name> master
  • 推送分支:git push <repo-name> <local-branch-name>:<remote-branch-name>

    • 如果省略远程分支名,则表示远程分支名和本地当前分支名一致,如此远程分支名不存在,则会在远程仓库新建该分支。

      示例:git push origin master

    • 如果省略本地分支名,则表示删除指定的远程分支,因为这等同于推送一个空的本地分支到远程分支。

      示例:git push origin :dev

    • 如果本地分支名和远程分支名都省略,则表示将当前分支推送到追踪的远程分支。

      示例:git push origin

    push 命令最后加上--tags会推送未曾推送过的标签,参看标签管理

    • 只推送当前分支:git push(如果当前分支只有一个追踪分支时可以使用)
    • 推送全部分支:git push -all origin
    • 推送并指定默认远程跟踪分支:git push -u <repo-name> <branch-name>
    • 强制推送:git push --force origin (使用本地快照版本强制覆盖到远程仓库)
  • 获取分支

    • 获取当前分支新内容:git fetch <repo-name> 从远程仓库下载新内容
    • 拉取并合并到当前分支:git pull <repo-name> 从远程仓库下载新内容并尝试合并 到本地当前分支(相当于先 fetch 获取新内容,然后 merge)

工作区存储

暂存当前工作区以操作新分支。

  • 存储工作区:git stash

  • 列出存储的工作区:git stash list

  • 恢复存储的工作区

    • 恢复工作区且保留工作区内容:git stash apply

      • 恢复指定指定编号的工作区:git stash apply stash@{number}( number 是一个 数字)

        编号可用git stash list命令查看。

    • 恢复工作区并删除工作区内容:git stash pop

  • 删除工作区:git stash drop

标签管理

提交快照时的 commit id 是一串数字 + 字母(hash code ),难以记忆,使用相对不变 ,tag 可以对提交打上容易记住的标签。

  • 查看标签:

    • 列出所有标签:git tag
    • 查看指定标签信息:git show <tag-name>
  • 添加标签

    可以在打标签命令后添加 -m "about"(about 是标签注解 ) 给标签添加注解。

    • 给当前分支打标签:git tag <tag-name>
    • 给指定快照打标签:git tag <tag-name> <commit-id>
    • 使用 GPG 签名打标签:git tag -s <tag-name>
  • 推送标签

    创建的标签都只存储在本地,不会自动推送到远程仓库。

    • 推送一个本地标签:git push <repository-name> <tag-name>
    • 推送全部未推送过的本地标签:git push <repository-name> --tags
  • 删除标签

    • 删除一个本地标签:git tag -d <tag-name>
    • 删除一个远程标签:git push <repository-name> :refs/tags/<tag-name>

git 配置

忽略规则

创建 .gitignore 文件,添加特定匹配规则就可以禁止相应的文件推送到远程仓库。 github 提供的 .gitingore 文件

windows 下:在资源管理器里新建一个 .gitignore 文件,系统会提示必须输入文件名,可 在文本编辑器里 “ 保存 ” 或者 “ 另存为 ” 就可以把文件保存为 .gitignore 了。

  • 校验 .gitingore 文件:git check-ignore
  • 校验指定规则:git check-ignore -v <rule>
  • 强制添加被忽略的文件:git add -f <file-name>
  • .gitingore 编写:
    • #注释
    • 一行一条
    • 同名匹配
    • 可使用通配符

配置

git 的配置文件在~/.gitconfig,仓库的配置文件是仓库内的.git/config

可运行git help git configman git查看更多帮助信息。

官方文 档git-config Manual Pagee

部分设置命令:

加上--global参数,则设置内容对当前用户生效,不加--global则对当前仓库生效。

  • 检查配置情况:git config --list

  • 设置默认编辑器,如 nano: git config --global core.editor nano

  • 设置默认对比工具,如 meld:git config --global merge.tool meld

  • 彩色输出:git config --global color.ui true

  • 中文文件名显示:git config --global core.quotepath false(避免中文显示成数字 )

  • 显示历史记录时每个提交的信息显示一行: git --global config format.pretty oneline

  • 设置用户名和电子邮箱

    git config --global user.name "your name"
    git config --global user.email "email@example.com
  • 协议更换

    如 https 替代 git 协议

    git config --global url."https://".insteadof "git://"
    git config --global url."https://github.com/".insteadof "git@github.com:"
  • 设置代理

    如使用 socks5,本地 ip 和端口是 127.0.0.1:1080

    git config --global http.proxy socks5://127.0.0.1:1080
    git config --global https.proxy socks5://127.0.0.1:1080
    #取消设置的代理
    git config --global --unset http.proxy
    git config --global --unset https.proxy
  • 设置命令别名:git config --global alias.<another name> status

    git config --global alias.ci commit
    git config --global alias.br branch
    git config --global alias.unstage 'reset HEAD'
    git config --global alias.graph 'log --graph --oneline --decorate'
    

    git 服务简易搭建

  1. 安装 git、openssh ,开启 ssh 服务:systemctl start sshd && systemctl enable sshd

  2. 创建运行 git 服务的用户(可选)

  3. 初始化 Git 仓库:git init --bare <name.git>(服务器上的 Git 仓库通常都以 .git 结尾)

    如果要从已经存在的仓库克隆一份作为新的裸仓库:git clone --bare <repo-name> <new-repo-name>.git (注意:此命令会只复制出原仓库中的.git目录)

    也可以将原仓库的.git目录复制并改名成一个新仓库:cp <repo-name>/.git <new-repo-name>.git

    如果服务器的 ssh 服务更改了默认使用端口,参照前文 “ 远程关联 - 从远程仓库克隆 ” 中的使用方法。

  1. 安全管理
  • 非对称加密

    在客户机使用ssh-keygen生成非对称加密的公私钥,在服务器上运行 git 的用户 的~/.ssh/authorized_kesys文件中,添加有客户机公钥。(注意:保证.ssh文件夹 权限为 700 以及authorized_kesys文件权限为 600,公钥一行一个。)

  • git 文件夹权限

    git 仓库文件夹的权限设置为 755(即rwxr-xr-x ),不允许其他用户更改(仅通过添 加 SSH 公钥来添加允许更改的客户端,这些被允许的客户端是通过执行 git 服务的用户 来获取写入权限的)。

  • ssh 权限

    ssh 配置文件位于/etc/ssh/sshd_config ,如更改默认端口,使用白名单策略等等。

  • shell 权限

    假如 git 服务的执行用户名为git,编辑/etc/passwd文件,找到git所在行,将行 中/bin/bash字样(根据不同 shell,也可能是bin/zsh等等) 改为bin/git-shell

变基合并(仅在你修改过后)

clone 自己的项目

git clone --branch master https://github.com/maboloshi/hexo-theme-next.git

Cloning into 'hexo-theme-next'... remote: Counting objects: 7563, done. remote: Total 7563 (delta 0), reused 0 (delta 0), pack-reused 7563Receiving obje cts 100% (7563/7563), 11.23 MiB | 910.00 KiB/s Receiving objects: 100% (7563/7563), 11.33 MiB | 913.00 KiB/s, done. Resolving deltas: 100% (4150/4150), done. Checking out files: 100% (270/270), done.

查看项目当前remote

cd hexo-theme-next git remote -v

origin https://github.com/maboloshi/hexo-theme-next.git (fetch) origin https://github.com/maboloshi/hexo-theme-next.git (push)

添加 new remote, "upstream" (上游):

git remote add upstream https://github.com/iissnan/hexo-theme-next.git git remote -v

origin https://github.com/maboloshi/hexo-theme-next.git (fetch) origin https://github.com/maboloshi/hexo-theme-next.git (push) upstream https://github.com/iissnan/hexo-theme-next.git (fetch) upstream https://github.com/iissnan/hexo-theme-next.git (push)

将该远程的所有分支获取到远程跟踪分支中,例如upstream/master

git fetch upstream

remote: Counting objects: 4469, done. remote: Compressing objects: 100% (5/5), done. Receiving objects: 99% (4425/4469remote: Total 4469 (delta 914), reused 916 (de

Receiving objects: 100% (4469/4469), 1.67 MiB | 725.00 KiB/s, done. Resolving deltas: 100% (2861/2861), completed with 184 local objects. From https://github.com/iissnan/hexo-theme-next

  • [new branch] dev -> upstream/dev
  • [new branch] master -> upstream/master
  • [new branch] servant -> upstream/servant
  • [new branch] testing -> upstream/testing
  • [new tag] v5.1.1 -> v5.1.1
  • [new tag] v5.1.2 -> v5.1.2
  • [new tag] v5.1.3 -> v5.1.3
  • [new tag] v5.1.4 -> v5.1.4

确保你在你的master分支上

git checkout master

Already on 'master' Your branch is up to date with 'origin/master'.

重写你的master分支,以便任何尚未处于upstream/master的提交都在该分支之上重播:

git rebase upstream/master

First, rewinding head to replay your work on top of it... Applying: 添加 disqus 点击载入功能. Using index info to reconstruct a base tree... M _config.yml M layout/_partials/comments.swig A layout/_scripts/third-party/comments/disqus.swig Falling back to patching base and 3-way merge... CONFLICT (modify/delete): layout/_scripts/third-party/comments/disqus.swig delet ed in HEAD and modified in 添加 disqus 点击载入功能.. Version 添加 disqus 点击载 入功能. of layout/_scripts/third-party/comments/disqus.swig left in tree. Auto-merging layout/_partials/comments.swig CONFLICT (content): Merge conflict in layout/_partials/comments.swig Auto-merging _config.yml CONFLICT (content): Merge conflict in _config.yml error: Failed to merge in the changes. Patch failed at 0001 添加 disqus 点击载入功能. The copy of the patch that failed is found in: .git/rebase-apply/patch

Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase --continue". You can instead skip this commit: run "git rebase --skip". To abort and get back to the state before "git rebase", run "git rebase --abort".

冲突解决方式

  1. 手动解决后继续合并 git add --all . git rebase --continue

  2. 跳过冲突 git rebase --skip

  3. 取消并恢复到合并之前 git rebase --abort

如果你不想重写主分支的历史记录(例如,因为其他人可能已经克隆了它),那么你应该用 git merge upstream / master替换上一个命令。 但是,为了进一步提出尽可能干净的请求,最好重新绑定。

如果您已将分支重定位到上游/主数据库,则可能需要强制推送以将其推送到GitHub上自己分叉的存储库。 你可以这样做:

合并后push到远程端 git push -f origin master

您只需在重新组装之后首次使用-f。 snip20180315_6

git push origin master

Tips: git协议由https变为ssh git remote set-url origin git@github.com:someaccount/someproject.git

修改最后一次comment git add/rm git commit --amend

添加并初始化子模块

# 添加 Git submodule 仓库
git submodule add <remote_url> <local_path>

# 对于clone一个有submodule的仓库,是不会把submodule也clone下来
# 需要额外的步骤:
# 1. 注册submodule到.git/config里 (初始化)
git submodule init
# 2. clone submodule
git submodule update --recursive
# 上面两步合并等价于下面
git submodule update --init --recursive

# 如果修改了.gitmodule的remote url,使用下面的命令更新submodule的remote url
git submodule sync

删除子模块

使用git 1.7.8或更高版本克隆的子模块会在本地项目中留下至多四条痕迹。 删除这四条曲线的过程由以下三条命令给出:

# Remove the submodule entry from .git/config
git submodule deinit -f path/to/submodule

# Remove the submodule directory from the superproject's .git/modules directory
rm -rf .git/modules/path/to/submodule

# Remove the entry in .gitmodules and remove the submodule directory located at path/to/submodule
git rm -f path/to/submodule

出处:https://stackoverflow.com/questions/1260748/how-do-i-remove-a-submodule/36593218#36593218

1. Clone your fork:

git clone https://github.com/zorbax/REPO.git

2. Add remote from original repository in your forked repository:

cd REPO
git remote add upstream https://github.com/ORIGINAL-DEV-USERNAME/REPO.git

git fetch upstream

3. Updating your fork from original repo to keep up with their changes:

git pull upstream master

git push

In your local clone of your forked repository, you can add the original GitHub repository as a "remote". ("Remotes" are like nicknames for the URLs of repositories - origin is one, for example.) Then you can fetch all the branches from that upstream repository, and rebase your work to continue working on the upstream version. In terms of commands that might look like:

Add the remote, call it "upstream":

git remote add upstream https://github.com/whoever/whatever.git

Fetch all the branches of that remote into remote-tracking branches, such as upstream/master:

git fetch upstream

Make sure that you're on your master branch:

git checkout master

Rewrite your master branch so that any commits of yours that aren't already in upstream/master are replayed on top of that other branch:

git rebase upstream/master

If you don't want to rewrite the history of your master branch, (for example because other people may have cloned it) then you should replace the last command with git merge upstream/master. However, for making further pull requests that are as clean as possible, it's probably better to rebase.

If you've rebased your branch onto upstream/master you may need to force the push in order to push it to your own forked repository on GitHub. You'd do that with:

git push -f origin master

You only need to use the -f the first time after you've rebased.

Git 创建空分支

准备工作,clone 项目,然后进入项目目录。

创建一个(空)分支

使用参数 --orphan,这个参数的主要作用有两个,一个是拷贝当前所在分支的所有文件,另一个是没有父结点,可以理解为没有历史记录,是一个完全孤立(独立背景干净)的分支。

# 创建并切换到一个 xxx 的分支,这个分支是孤立的
git checkout --orphan <新的分支名>

使用 git help checkout命令可以看到相关介绍

error: The following untracked working tree files would be overwritten by checkout:

则执行git clean -d -fx,该操作会清除所有git clone下的所有文件,只剩.git目录。 然后再重新执行一遍git checkout --orphan <新的分支名>

清空当前分支下的所有文件

这个操作不会影响别的分支,特别是你的。

# 清除所有git文件历史,为了空白分支
git rm -rf .

git add README.md
git commit -m "提交信息"

push 新建的分支到远程仓库

#推送远程分支
git push origin <新的分支名>

至此,创建新分支操作已经完成,可以在 github/gitee 页面查看。

@maboloshi
Copy link
Author

maboloshi commented Oct 3, 2019

删除远程分支Test_Branch
To delete Test_Branch from remote as well, execute:

git push origin --delete Test_Branch

删除本地分支Test_Branch

git push --delete Test_Branch

@maboloshi
Copy link
Author

压缩提交

git rebase -i <hash>

You now can rebase all commits up to root(第一个提交),

git rebase -i --root

@maboloshi
Copy link
Author

git删除未跟踪文件

# 删除 untracked files
git clean -f
 
# 连 untracked 的目录也一起删掉
git clean -fd
 
# 连 gitignore 的untrack 文件/目录也一起删掉 (慎用,一般这个是用来删掉编译出来的 .o之类的文件用的)
git clean -xfd
 
# 在用上述 git clean 前,墙裂建议加上 -n 参数来先看看会删掉哪些文件,防止重要文件被误删
git clean -nxfd
git clean -nf
git clean -nfd

@maboloshi
Copy link
Author

maboloshi commented Aug 23, 2020

摘录自: https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E5%86%99%E5%8E%86%E5%8F%B2

修改多个提交信息

为了修改在提交历史中较远的提交,必须使用更复杂的工具。 Git 没有一个改变历史工具,但是可以使用变基工具来变基一系列提交,基于它们原来的 HEAD 而不是将其移动到另一个新的上面。 通过交互式变基工具,可以在任何想要修改的提交后停止,然后修改信息、添加文件或做任何想做的事情。 可以通过给 git rebase 增加 -i 选项来交互式地运行变基。 必须指定想要重写多久远的历史,这可以通过告诉命令将要变基到的提交来做到。

例如,如果想要修改最近三次提交信息,或者那组提交中的任意一个提交信息, 将想要修改的最近一次提交的父提交作为参数传递给 git rebase -i 命令,即 HEAD~2^HEAD~3。 记住 ~3 可能比较容易,因为你正尝试修改最后三次提交;但是注意实际上指定了以前的四次提交,即想要修改提交的父提交:

$ git rebase -i HEAD~3

再次记住这是一个变基命令——在 HEAD~3..HEAD 范围内的每一个修改了提交信息的提交及其 所有后裔 都会被重写。 不要涉及任何已经推送到中央服务器的提交——这样做会产生一次变更的两个版本,因而使他人困惑。

运行这个命令会在文本编辑器上给你一个提交的列表,看起来像下面这样:

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# 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

需要重点注意的是相对于正常使用的 log 命令,这些提交显示的顺序是相反的。 运行一次 log 命令,会看到类似这样的东西:

$ git log --pretty=format:"%h %s" HEAD~3..HEAD
a5f4a0d added cat-file
310154e updated README formatting and added blame
f7f3f6d changed my name a bit

注意其中的反序显示。 交互式变基给你一个它将会运行的脚本。 它将会从你在命令行中指定的提交(HEAD~3)开始,从上到下的依次重演每一个提交引入的修改。 它将最旧的而不是最新的列在上面,因为那会是第一个将要重演的。

你需要修改脚本来让它停留在你想修改的变更上。 要达到这个目的,你只要将你想修改的每一次提交前面的 ‘pick’ 改为 ‘edit’。 例如,只想修改第三次提交信息,可以像下面这样修改文件:

edit f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

当保存并退出编辑器时,Git 将你带回到列表中的最后一次提交,把你送回命令行并提示以下信息:

$ git rebase -i HEAD~3
Stopped at f7f3f6d... changed my name a bit
You can amend the commit now, with

       git commit --amend

Once you're satisfied with your changes, run

       git rebase --continue

这些指令准确地告诉你该做什么。 输入

$ git commit --amend

修改提交信息,然后退出编辑器。 然后,运行

$ git rebase --continue

这个命令将会自动地应用另外两个提交,然后就完成了。 如果需要将不止一处的 pick 改为 edit,需要在每一个修改为 edit 的提交上重复这些步骤。 每一次,Git 将会停止,让你修正提交,然后继续直到完成。

给已提交信息添加GPG签名

摘录: https://superuser.com/questions/397149/can-you-gpg-sign-old-commits

步骤与修改多个提交信息类似, 其中修补命令(git commit --amend)改为如下

  • 提交内容和信息不需要修改时:
$ git commit --amend --no-edit -n -S
  • 提交内容和或信息需要修改时:
$ git commit --amend -S

@maboloshi
Copy link
Author

修改已提交的commit的用户名邮箱

采编自: https://www.jianshu.com/p/7def4f387e9f

修改最近一次

git commit --amend --author="userName <userEmail>"

注意不能缺少< >
此指令仅能更新最近的一次commit的用户名邮箱

批量修改

git filter-branch --env-filter '
if [ "$GIT_AUTHOR_NAME" = "oldName" ]
then
export GIT_AUTHOR_NAME="newName"
export GIT_AUTHOR_EMAIL="newEmail"
fi
' ref..HEAD

git filter-branch --env-filter '
if [ "$GIT_COMMITTER_NAME" = "oldName" ]
then
export GIT_COMMITTER_NAME="newName"
export GIT_COMMITTER_EMAIL="newEmail"
fi
' ref..HEAD 

分别修改AUTHOR_NAME和COMMITTER_NAME,不太清楚二者的区别。
好像GIT_AUTHOR是用来在push时验证用户信息的,所以可以只修改第一个。
其中ref是起始commit的sha-1的简写,指定生效范围为此commit_(exclude)到HEAD(include)_,避免对整个git的提交历史的修改,可以缩短运行时间。
这一方法不知会不会影响已push过的commit,不过一般都是因为用户名没有push权限,才需要纠正commit的用户名,所以无需在意。

如果提示

Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f 

可以加上-f,即git filter-branch -f --env-filter,不明原理,但加上了确实管用,谨慎使用。

@maboloshi
Copy link
Author

提交没有提交信息

git commit -a --allow-empty-message -m ""

创建别名nccommit

git config --global alias.nccommit 'commit -a --allow-empty-message -m ""'

@maboloshi
Copy link
Author

Git 工具 - 凭证存储

参考: Git 工具 - 凭证存储

On mac: 将密钥存储在系统钥匙链

git config --global credential.helper osxkeychain

On Win10: 将密钥存储在系统Windows 凭据

git config --global credential.helper wincred

注: 可通过控制面板 > 所有控制面板项 > 凭据管理器 查看

明码存储在本文文件中:

git config --global credential.helper store

注:可使用 --file <path> 参数,自定义存放密码的文件路径(默认是 ~/.git-credentials

缓存在内存中:

git config --global credential.helper cache

注:可使用--timeout <seconds> 参数,设置后台进程的存活时间(默认是 “900”,也就是 15 分钟)

@maboloshi
Copy link
Author

git rebase upstream/master --autostash

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment