git常用命令及使用

# 简介

Git是分布式版本控制系统,那么它就没有中央服务器的,每个人的电脑就是一个完整的版本库,这样工作的时候就不需要联网了,因为版本都是在自己的电脑上。 分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。

分布式及集群的定义

  • 分布式:是指将一个业务拆分不同的子业务,分布在不同的机器上执行。 常用的分布式就是在负载均衡 (opens new window)服务器后加一堆web服务器,然后在上面搞一个缓存服务器来保存临时状态,后面共享一个数据库;
  • 集群:是指多台服务器集中在一起,实现同一业务,可以视为一台计算机。 多台服务器组成的一组计算机,作为一个整体存在,向用户提供一组网络资源,这些单个的服务器就是集群的节点;

# 几个专用名词

image-20200827231821368

  • Workspace:工作区
  • Index / Stage:暂存区
  • Repository:仓库区(或本地仓库)
  • Remote:远程仓库

# Git与SVN区别

# Git优点:

1、分布式,每个参与开发的人的电脑上都有一个完整的仓库,不担心硬盘出问题; 2、在不联网的情况下,照样可以提交到本地仓库,可以查看以往的所有log,等到有网的时候,push到远程即可; 3、非常强大的分支管理功能。 4、Git的内容的完整性要优于SVN: GIT的内容存储使用的是SHA-1哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。

# Git缺点:

权限管理不是很方便,需要安装插件gitolite,配置有点麻烦,或者直接使用gitlab管理。

# SVN优点

1、较好的权限管理功能,可以精确控制每个目录的权限; 2、使用相对git要简单一点。

# SVN缺点

1、集中式,如果中心服务器出现问题,所有人都不能正常干活,恢复也很麻烦,因为SVN记录的是每次改动的差异,不是完整文件; 2、分支功能没有git强大; 3、速度没有git快,如果有五个分支,是把五个分支的文件全部拷下来; 4、必须联网才能commit。

# 配置

# 配置账户信息

# 显示当前的Git配置
git config --list
git config [--global] user.name x
git config [--global] user.email x

git config --global user.name samy 
git config --global user.email "samyit@foxmail.com"
# 类似npm的设置:
npm config set init.author.name samy
npm config set init.author.email samyit@foxmail.com
1
2
3
4
5
6
7
8
9
10

# 全局配置.gitignore

方式一:创建~/.gitignore_global文件,把需要全局忽略的文件写入该文件,语法和.gitignore一样

A:在~/.gitconfig中引入.gitignore_global文件

[core] **

excludesfile = /Users/samy/.gitignore_global **

也可以通过git config --global core.excludesfile/Users/reon/.gitignore_global命令来实现

.git/info/exclude失效

方式二:【推荐】

vim .git/info/exclude

添加要忽略的文件夹及文件即可;

# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
/sample/
/ztest/
*/**/apidoc.json
1
2
3
4
5
6
7
8
9

# 设置默认区分大小写

默认情况下git是忽略区分大小写的,多人合作的情况下不规范很容易造成问题,所以开启区分大小写。

git config --global core.ignorecase false #全局开启
git config core.ignorecase false #在仓库命令行中执行

git config --list #找到core.ignorecase=false即说明修改完毕

#改回默认
git config core.ignorecase true
# or
git config --unset core.ignorecase
1
2
3
4
5
6
7
8
9

# 常用命令

# 最常用命令

gitk . 查看记录UI; 在mac得再安装git-ui; brew install git-ui

git status
git diff
git add .
git commit -m ""
git checkout -- xxxfile #移除修改后没有提交的文件
git stash (pop) #暂时将未提交的变化移除,稍后再移入
git stash  //git stash pop
git rm -r --cached  "db/" #如何移除某文件夹的版本控制
1
2
3
4
5
6
7
8

# 封装成脚本

最常用命令及封装成脚本;

git pull origin dev #取回远程origin仓库下dev分支,再与当前分支合并
git add . #添加当前目录的所有文件到暂存区
git commit -m “test” #提交暂存区到仓库区
git push origin #上传本地当前分支到远程仓库
1
2
3
4

gitsync.sh

#!/bin/bash
git pull
git add . 
git commit -m "${1}"
git push
1
2
3
4
5

# 命令汇总说明【要点】

#添加本地仓库:
#添加url
git remote add origin git@github.com:samyxx/test.git
git remote add work https://github.com/work-xxxx/test.git #或者git@xxx
#修改url,更强大 【推荐】
git remote set-url origin git://xxxxxxxx.git
#查看git 配置信息
git config --list # 查看是否配置成功

#检查修改仓库列表
git remote (-v) #-v(详细,包括fetch push)
git fetch/pull origin (master)

#拉取和提交指定仓库代码 远程分支
git pull work #拉取全部 
git pull work dev #拉取指定分支
git push origin dev
# 下载远程仓库的所有变动
git fetch [remote] 

#切换分支:
git checkout dev
git checkout -t work/dev #切换指定仓库分支(-t),根据线上新建本地新仓库;
git checkout -t origin/2.0.0
git checkout -b 本地分支名 origin/远程分支名
#克隆指定分支
git clone -b mac https://github.com/xxx/xxx.git

#复制创建个临时分支
git checkout -b dev-tmp 
#创建指定临时分支
git checkout -b tmp-dev 421ec79394cbd1cd6a03980e68c3e06cdb4ee6a5

#分支删除
git branch -d dev-tmp  #删除本地分支
git push origin --delete dev-tmp  #可以删除远程分支dev-tmp

#还原分支:
git revert HEAD #[推荐]
git reset 
git reset --hard HEAD^ #删除最后一次远程提交;默认是soft模式回退到上一个版本
git chekckout head . #撤销未提交的文件
git clean -df #删除当前目录下没有被track过的文件和文件夹

#修改分支名:
#重命名git本地分支
git branch -m old_local_branch_name new_local_branch_name 
#重命名git远程分支
#Step1:重命名远程分支对应的本地分支
git branch -m old_local_branch_name new_local_branch_name
#step2:删除远程分支
git push origin :old_local_branch_name
#step3:重新推送新命名的本地分支
git push origin new_local_branch_name
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

# 查看log

git log
git log dev-tmp #查看指定分支日志
git show f36801544670e00b2f59a28e19017d2786c4085e
1
2
3

# tag操作

git tag #显示本地tag
git tag -d mytag #删除本地tag
git push origin :refs/tags/mytag #删除远程tag
1
2
3

# 分支合并

单个记录合并及全部合并

git branch -r #查看本地分支
git pull work edu-1.6 #拉取公司分支到本地,同时也创建了本地edu-1.6分支
#在dev分支,修改代码后提交;
git log #后复制commit id
git checkout (-b,创建) edu-1.6 #切换到edu-1.6分支
git log #检查edu-1.6分支的log,
git cherry-pick 0775dbd9af8f4a7b6bbdd81c4081e75445aef5f8 #把dev修改部分的commitid合并到edu-1.6分支上

#结合github上的项目分支日志记录比对;
git log #检查edu-1.6分支的log是否合并上最新修改的log
git push origin edu-1.6 #提交到自己线上仓库,待pr到公司
git merge dev #全部把dev的合并到edu-1.6分支
git checkout dev #切换到源分支

#变基; rebase操作的特点:把分叉的提交历史“整理”成一条直线,看上去更直观。缺点是本地的分叉提交已经被修改过了
git log --graph --pretty=oneline --abbrev-commit # gitk .
git push orgin master
git pull
git status
git log --graph --pretty=oneline --abbrev-commit
git rebase
git push orgin master
git log --graph --pretty=oneline --abbrev-commit
#rebase操作可以把本地未push的分叉提交历史整理成直线;
#rebase的目的是使得我们在查看历史提交的变化时更容易,因为分叉的提交需要三方对比
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# git pull

git push <远程主机名> <本地分支名>:<远程分支名>

# 从远程仓库拉取代码并合并到本地,可简写为 git pull 等同于 git fetch && git merge 
git pull <远程主机名> <远程分支名>:<本地分支名>
# 使用rebase的模式进行合并
git pull --rebase <远程主机名> <远程分支名>:<本地分支名>

git pull = git fetch + git merge FETCH_HEAD 
git pull --rebase =  git fetch + git rebase FETCH_HEAD 
1
2
3
4
5
6
7
  • git pull = git fetch + git merge

    • git pull origin dev 动作是去获取远程仓库中dev分支上的commits,然后把origin/develop merge到你目前check out下来的分支中

    • git pull 就是省去了指定分支,默认就是去你之前check out 的分支上去操作,比如,如果你本地的check out的分支 track的就是origin/dev,那么git pull就等于git pull origin dev

    • 示例:

      $ git pull origin next:master
      # 如果远程分支(next)要与当前分支合并,则冒号后面的部分可以省略。上面命令可以简写为:
      $ git pull origin next
      # 取回origin/next分支,再与当前分支合并。实质上,这等同于先做git fetch,再执行git merge。
      $ git fetch origin
      $ git merge origin/next
      
      1
      2
      3
      4
      5
      6
  • git pull --rebase 用在合并代码的时候其作用就是在一个随机创建的分支上处理冲突,避免了直接污染原来的分区;合并冲突后的处理命令;

    git add .
    git rebase --continue
    git push
    
    1
    2
    3

# git fetch

git pull 不同的是git fetch操作仅仅只会拉取远程的更改,不会自动进行merge操作。对你当前的代码没有影响

# 获取远程仓库特定分支的更新
git fetch <远程主机名> <分支名>
# 获取远程仓库所有分支的更新
git fetch --all
1
2
3
4

# git merge/rebase

rebase 翻译为变基,他的作用和 merge 很相似,用于把一个分支的修改合并到当前分支上。

不同于 git rebase的是,git merge 在不是 fast-forward(快速合并)的情况下,会产生一条额外的合并记录,类似Merge branch 'xxx' into 'xxx'的一条提交信息。

# 区别

image-20210531151838328.png

WechatIMG2.png

image-20200827231902785

       D---E test
      /
 A---B---C---F--- master
        D--------E
      /          \
 A---B---C---F----G---   test, master #在master执行git merge test,然后会得到的结果;
 A---B---D---E---C‘---F‘---   test, master#在master执行git rebase test,然后得到的结果;
1
2
3
4
5
6
7
  • merge操作会生成一个新的节点,之前的提交分开显示
  • 而rebase操作不会生成新的节点,是将两个分支融合成一个线性的提交。想要更好的提交树,使用rebase操作会更好一点。这样可以线性的看到每一次提交,并且没有增加提交节点。

# 基本原则

  1. 下游分支更新上游分支内容的时候使用 rebase; 比如:在dev上合并master;
  2. 上游分支合并下游分支内容的时候使用 merge ; 比如:在master上合并dev;
  3. 更新当前分支的内容时一定要使用 --rebase 参数
git checkout master
git pull
git checkout local
git rebase -i HEAD~2  #合并提交 --- 2表示合并两个
git rebase -i 36224db #指定commitid
git rebase master---->解决冲突--->git rebase --continue
git checkout master
git merge local
git push
1
2
3
4
5
6
7
8
9

# 合并冲突处理

merge 操作遇到冲突的时候,当前merge不能继续进行下去。手动修改冲突内容后,add 修改,commit 就可以;

而rebase 操作的话,会中断rebase, 同时会提示去解决冲突。解决冲突后,将修改add后执行git rebase –continue继续操作,或者git rebase –skip忽略冲突。

#解决冲突; 在rebase的过程中,也许会出现冲突(conflict). 在这种情况,Git会停止rebase并会让你去解决 冲突;在解决完冲突后,用"git-add"命令去更新这些内容的索引(index), 然后,无需执行 git-commit,只要执行:
git add
git rebase --continue
git rebase --abort #在任何时候,可以用--abort参数来终止rebase的行动,并且操作的分支会回到rebase开始前的状态。
1
2
3
4

# git merge

# --ff 是指fast-forward命令,当使用ff模式进行合并时,将不会创造一个新的commit节点。
# --no-ff,保留合并分支的提交记录,一般主干用的比较多.
# --ff-only 除非当前HEAD节点为最新节点或者能够用ff模式进行合并,否则拒绝合并并返回一个失败状态。
# --squash 则类似 rebase squash,可以把合并多个 commit 变成一个
git merge --no-ff branchName
1
2
3
4
5

# git rebase

变基在项目中算是很频繁的,为什么这么说。

比如你开发一个新的 feature, 遵循最小化代码提交的理念。

整个功能开发完毕的时侯,会有非常多的 commit,用 rebase 可以让我们的commit记录很干净

# 带 -i 可以进入交互模式,效果如下
git rebase -i git-sha1|branch(HEAD)

# 若是中间毫无冲突,变基则一步到位,否则需要逐步调整。
git rebase --continue # 提交变更后继续变基下一步
git rebase --skip # 引起冲突的commits会被丢弃,continue提示没有需要改动的也可以用这个跳过
git rebase --abort # 若是变基改残废了,但是走到一半,可以彻底回滚变基之前的状态
1
2
3
4
5
6
7

![image.png](./_image\01.git-cmd\16ed58cd790c1d8a)

  • pick: 是保留该 commit(采用)
  • edit: 一般你提交的东西多了,可以用这个把东东拿回工作区拆分更细的 commit
  • reword: 这个可以重新修改你的 commit msg
  • squash: 内容保留,把提交信息往上一个 commit 合并进去
  • fixup: 保留变动内容,但是抛弃 commit msg
  • drop: 用的比较少,无用的改动你会提交么!!!

就是把回滚和打标签这些放到变基中简化操作。

温馨提示:

  • 本地提交之前,最好把基准点变为需要合并的分支,这样提交 PR/MR 的时侯就不会冲突(本地来解决冲突)
  • 不要在公共分支上变基!!!一变其他协作者基本都一堆冲突!除非你们有很清晰的分支管理机制

# git cherry-pick

git cherry-pick可以理解为”挑拣”提交,和 merge 合并一个分支的所有提交不同的是,它会获取某一个分支的单笔提交,并作为一个新的提交引入到你当前分支上。可以从多个分支同时挑取部分需要的 commit 合并到同一个地方去

这和变基有点类似,但是仅仅类似,挑过来的 commit 若是没有冲突则追加。有冲突会中断,解决后 --continue; 过程中,如果出现冲突,解决冲突后 进行git add,接着执行 git cherry-pick --continue

# 在当前分支挑其他分支的 commit,把那部分的变动那过来
git cherry-pick commit-sha1

# 支持一次性拿多个
git cherry-pick master~4 master~2

# 支持区间, 区间中间是 .. 
git cherry-pick startGitSha1..endGitSha1

# --continue:继续 pick,一般有冲突解决后才需要这样
# --skip:跳过这次进入队列下一次行为 
# --abort : 完全放弃 pick,恢复 pick 之前的状态
# --quit: 未冲突的自动变更,冲突的不要,退出这次 pick
# 这几个状态跟变基差不多,解决冲突继续,跳过处理,放弃这次pick,不输出错误
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# git remote

需要考虑维护多个地方仓库的时侯会考虑,或者修改仓库源的时侯

# 常规关联本地 git init 到远程仓库的姿势
git remote add origin url

# 新增其他上游仓
git remote add github url

# 修改推送源
git remote set-url  origin(或者其他上游域) url
1
2
3
4
5
6
7
8

# git log

查看日志,常规操作

# 输出概要日志,这条命令等同于
# git log --pretty=oneline --abbrev-commit
git log --oneline

# 指定最近几个提交可以带上 - + 数字
git log --oneline -5

# 提供类似 GUI 工具的 log 展示 【推荐】
git log --graph --date=relative --pretty=tformat:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%an %ad)%Creset'
1
2
3
4
5
6
7
8
9

![image.png](./_image/01.git-cmd\16ed58cd77c1b305)

# git reflog

这个命令的强大之处,是记录了所有行为,包括你 rebase,merge, reset 这些

当我们不小心硬回滚的时侯,或变基错了都可以在这里找到行为之前的commit,然后回滚。

当然这个时间回溯也只在本地有用,你推送到远程分支的破坏性改动,该凉还是得凉。

# 最近五次行为,不带-n 则默认所有
git reflog -5
1
2

# git status

查看工作区状态的东东,不如GUI直观,但是命令行也有一些用的

# 等同 git status --long,查看当前工作区暂存区变动
git status

# 概要信息 (--short)
git status -s

# 查询工作区中是否有stash存在(暂存的东西),有则提醒该工作区有几个 stash
git status  --show-stash
1
2
3
4
5
6
7
8

# git diff

  • 在终端比对用的不是很频繁,除了少量改动的时侯可能会用这个看看。
  • 其他情况下我更倾向于用 GUI 工具来看,因为比对更加直观。

# git branch

该命令用的最多的就是删除本地分支,重命名分支,删除远程分支了

# 分支删除,拷贝,重命名,参数若是大写就等同多了--force,强制执行
# -c, --copy : 复制分支,
# -C:等同于 --copy --force
# -d, --delete: 删除分支
# -m, --move:移动或者重命名分支
git branch -d branchName
git branch -M oldBranch newNameBranch

# 新建本地分支,但不切换
git branch <branch-name> 
# 查看本地分支
git branch
# 查看远程分支
git branch -r
# 查看本地和远程分支
git branch -a
# 删除本地分支
git branch -D <branch-nane>
# 重新命名分支
git branch -m <old-branch-name> <new-branch-name>

# 手动指定它的当前分支的上游分支,两个写法一致的
# 有关联一般也有取消关联,--unset-upstream
git branch --set-upstream-to=origin/xxx 
git branch --set-upstream-to origin xxx 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# git删除分支

1、删除本地分支

在删除分支的时候,会使用git branch --delete dev来执行.有时还会通过缩写git branch -d dev来代替,使用中我们发现还有git branch -D dev的写法,他们有什么区别呢?

  • -d是--delete的缩写,在使用--delete删除分支时,该分支必须完全和它的上游分支merge完成(了解上游分支,可以点击查看链接),如果没有上游分支,必须要和HEAD完全merge
  • -D是--delete --force的缩写,这样写可以在不检查merge状态的情况下删除分支
  • --force简写-f,作用是将当前branch重置到初始点(startpoint),如果不使用--force的话,git分支无法修改一个已经存在的分支.

2、删除远程分支

指令git push origin --delete branch,该指令也会删除追踪分支;

可以第三方工具,可以直接有提示是否删除远程分支,比如sourcetree

image-20220208165848258

# git add

  • git add -A 提交所有变化
  • git add -u 提交被修改(modified)和被删除(deleted)文件,不包括新文件(new)
  • git add . 提交新文件(new)和被修改(modified)文件,不包括被删除(deleted)文件

# git stash

暂存用的最多时侯就是你撸代码撸到一半,突然说有个紧急 BUG 要修正。或者别人在你这里需要帮忙排查代码,你这时候也会用到。强烈建议给每个 stash 添加描述信息!!!

# 缓存当前工作区的内容到stashName, save 这个现在不怎么推荐用,图方便也能用
# -a|--all: 除了未跟踪的文件,其他变动的文件都会保存
# -u|--include-untracked:包括没有添加到暂存区的文件
git stash save stashName
git stash -u save stashName

# 现在基本推荐用 push,因为有 pop,语义上和维护上更清晰
# 上面有的参数,它也有,还有-m 来备注这个 stash 的大概情况
git stash push -m "更改了 xx" 

# 有保存那肯定也有取用的
# pop: 取会删除对应的保存记录
# apply: 取但保留记录
# 0就是--index,这个东西哪里来的呢?
git stash apply stash@{0}
git stash pop stash@{0}

# 查看stash 保存记录
# eg: stash@{0}: On dev: 测试
git stash list


# 只想删除暂存记录怎么办:
# clear : 清空所有 stash
# drop: 清除指定的 stash
git stash clear # 慎用!
git stash drop stash@{0}

# 想看 stash 做了什么改动,类似简化版的git diff
git stash show stash@{0}

git stash # 把本地的改动暂存起来
git stash save "message" # 执行存储时,添加备注,方便查找。
git stash pop # 应用最近一次暂存的修改,并删除暂存的记录
git stash apply  # 应用某个存储,但不会把存储从存储列表中删除,默认使用第一个存储,即stash@{0},如果要使用其他个,git stash apply stash@{$num} 。
git stash list # 查看stash有哪些存储
git stash clear # 删除所有缓存的stash

## 基本操作步骤
git stash save "message..."
git stash list
git stash apply stash@${num}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# git commit

# 新修改的内容,添加到上次提交中,减少提交的日志  【要点】
# --no-edit:是跳过进入编辑器,直接提交
# git commit --amend这条命令等同于
# $ git reset --soft HEAD^
# $ ... do something  tree ...
# $ git commit -c ORIG_HEAD
git commit --amend --no-edit

# 跳过校验直接提交,包括任何 githooks  【要点】
git commit --no-verify -m "xxx"

# 带提交概要信息
git commit -m "xxx"

# 指定目录格式提交
# -t <file>, --template=<file>
# 也可以从全局或者项目级别指定提交的模板文件
# git config [--global] commit.template xxx
# 现在一般都是 用社区的npm规范包,commitizen 和 commitlint 来规范提交 【要点】
git commit -t templateFile

# 提交信息从文件读取,可以结合上面的一起用
git commit -F

# 等同于 git add . && git commit -m
git commit -am
# 对最近一次的提交的信息进行修改,此操作会修改commit的hash值
git commit --amend
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# git pull

git pull中用的最多是带--rebase(-r)的方式(变基形式拉取合并代码),保持分支一条线

默认的pull会走ff模式,多数情况会产生新的commit,部分参数与 merge提供一致。

# git push

当本地分支存在,远程分支不存在的时侯,可以这样推送关联的远程分支

# 这样会直接新建一个同名的远程分支
git push origin localbranch 

# 删除远程分支(--delete)   【要点】
git push -d origin branchName

# 推送所有标签  【要点】
git push --tags

# 推送 commit 关联的 tags  【要点】
git push --follow-tags

# 强制推送(--force)
git push -f origin branchName # 一般合理的项目,主干都做了分支保护,不会允许强推行为

# 有时候真的需要强推的时侯,但可不可以柔和一点呢?
# 就是当前远程分支和你本地一致,没有别人提交的情况下可以强推
# --force-with-lease: 若是远程有人提交,此次强推失败,反之成功
git push --force-with-lease
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# git checkout

用来切换到对应记录的,可以基于分支,提交,标签。

切提交和标签一般用来热修复或者老版本需要加新特性。

# 分支切换
git checkout dev # local branch

# 切换远程分支
git checkout origin/test # remote branch

# 基于远程分支创建本地分支,并跟踪对应来自 'origin' 的远程分支
git checkout --track origin/feature-test # new local branch wih remote branch

# 基于本地分支开出新分支
git checkout -b testbranch # new local branch with current branch

# 切换上一个分支  【要点】
git checkout -

# 彻底丢弃某个文件的改动 【要点】
git checkout -- file

# 放弃本地所有改动  【要点】
git checkout .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

不同的工作区域撤销更改

开发中,我们经常需要回退代码的操作,在不同的工作区域中,回退代码的方式也是不相同的

  • 想把x.js这个文件恢复到修改前的状态,即撤回工作区的修改,就可以使用 git checkout -- <filename>的命令,如果要撤回多个文件的修改,文件之间使用空格隔开;
  • 如果对文件进行了修改,并且已经提交到暂存区,这部分文件不想要的话,那么就可以通过 git reset <filename>的命令来对特定的文件进行撤销,git reset会撤回所有存在暂存区的文件

# git revert

  • 一般用于master 的代码回滚,因为多人在上面协作,
  • revert 可以平稳的回滚代码,但却保留提交记录,不会让协作的人各种冲突!
# 回滚到某个 commit
git revert commit-sha1

git revert <commit-id> # 针对普通commit
git revert <commit-id> -m # 针对merge的commit
1
2
3
4
5

此外 git revert 也可以回滚多次的提交

语法:git revert [commit-id1] [commit-id2] ... 注意这是一个前开后闭区间,即不包括 commit1,但包括 commit2。

# git reset

代码回滚中这个命令也是用的很多,而且是 --hard

# 硬性回滚,简单粗暴,直接抛弃回滚之后改动(log 还是有保留,内容不要而已)【要点】
git reset --hard commit_sha1

# 软性回滚, 跟 rebase 常规用法差不多的效果,可以把提交的东西丢回暂存区和工作区,
# HEAD 的指向改变会对应的 commit,之后再考虑怎么 commit
git reset --soft commit_sha1

# 软回滚一个版本,可以理解为撤销最近一次的 commit 【要点】
git reset --soft HEAD~1

# 清除暂存区但保留工作区变动。
git reset --mixed commit_sha1

# 保留工作区和暂存区之间的差异。
git reset --merge commit_sha1

# 保留工作区和HEAD之间的差异
git reset --keep commit_sha1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • git revert会新建一条 commit 信息,来撤回之前的修改。
  • git reset会直接将提交记录退回到指定的 commit 上。

# git rm

这个命令在旧的版本用的比较最多的姿势是为了重新索引.gitignore 的范围

当我们提交了一些文件远程,但是又想加到忽略文件禁止下次提交

主要是用到了 git rm 来实现

  • 带上 --cached 该命令并不会删除实际的代码文件。
  • 没有 --cached 会删除实际的代码和索引,慎用!
# 删除某个文件的索引 【要点】
# --cache 不会删除硬盘中的文件,只是 git 索引(缓存)的关系!!!
git rm --cache -- file

# 递归清除全部所有索引(也可以理解为缓存吧),这个姿势适合重新让.gitignore 新范围生效
git rm -r --cached .  
git add .
git commit -m "xxx"
1
2
3
4
5
6
7
8

先在.gitignore 里面增加新的忽略范围,然后再删除对应的缓存索引提交即可生效!

AME
       git-rm - Remove files from the working tree and from the index
SYNOPSIS
       git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...
DESCRIPTION
       Remove files from the index, or from the working tree and the index. git rm will not remove a file from just your working directory. (There is no option to remove a
       file only from the working tree and yet keep it in the index; use /bin/rm if you want to do that.) The files being removed have to be identical to the tip of the
       branch, and no updates to their contents can be staged in the index, though that default behavior can be overridden with the -f option. When --cached is given, the
       staged content has to match either the tip of the branch or the file on disk, allowing the file to be removed from just the index.

OPTIONS
       <file>...
           Files to remove. Fileglobs (e.g.  *.c) can be given to remove all matching files. If you want Git to expand file glob characters, you may need to shell-escape
           them. A leading directory name (e.g.  dir to remove dir/file1 and dir/file2) can be given to remove all files in the directory, and recursively all
           sub-directories, but this requires the -r option to be explicitly given.
       -f, --force
           Override the up-to-date check.
       -n, --dry-run
           Don't actually remove any file(s). Instead, just show if they exist in the index and would otherwise be removed by the command.
       -r
           Allow recursive removal when a leading directory name is given.
       --
           This option can be used to separate command-line options from the list of files, (useful when filenames might be mistaken for command-line options).
       --cached
           Use this option to unstage and remove paths only from the index. Working tree files, whether modified or not, will be left alone.
       --ignore-unmatch
           Exit with a zero status even if no files matched.
       -q, --quiet
           git rm normally outputs one line (in the form of an rm command) for each file removed. This option suppresses that output.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 忽略某个文件进入索引

# 这个 filename 就是单个文件
git rm --cached filename

# 若是要忽略某个目录所有文件则需要递归
git rm --cached -r dirname

# 若是要强制忽略再加个-f
git rm --cached -rf dirname
1
2
3
4
5
6
7
8

# 重新建立整个项目的索引【推荐】

若是有大量的文件且类型众多的需要忽略,一般重建整个git的索引是比较快的。不然就只能一点点的排除。

git rm -rf --cached .
git add .
git commit -m "refresh project git tree and index"
1
2
3

# git rev-parse

可以通过这个快速获取部分git 仓库的信息;在弄脚本的时侯就会从这里拿东西

# 获取最新有效的commit
# --short:显示七位的 sha1,不带就是全部
# --verify: 校验是否有效commit
# HEAD: 当前分支的head 指向
git rev-parse --short HEAD --verify #cb273e5

git rev-parse --abbrev-ref HEAD #master

# 显示仓库的绝对路径
git rev-parse --show-toplevel #eg: /Users/samy/Code/kkk/rat-ui

# 显示版本库.git 目录所在的位置
git rev-parse --git-dir

# 显示所有关联引用的 git sha1
git rev-parse --all
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 与nodejs的配合使用

获取当前分支的版本; git rev-parse --abbrev-ref HEAD

const branch = childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().replace(/\s+/, '')
const version = branch.split('/')[1]

// jenkins下的终极方案
git show-ref | grep $(git log --pretty=%h -1) | sed 's|.*/\(.*\)|\1|' | sort -u | grep -v HEAD

//let cmdStr = `git clone --depth 1 -b ${configBranch} ${url} ${repoTempPath}`;
1
2
3
4
5
6
7

打印版本脚本处理:

curDir=$(
    cd "$(dirname "$0")"
    pwd
)
versionDist=dist/web/version.txt
printVersion() {
  cd ${curDir}
  echo "==================================printVersion===================================="
  #   curBranch=`git symbolic-ref --short -q HEAD` # get current branch
  commitID=`git show-ref | grep $(git log --pretty=%h -1) | sed 's|.*/\(.*\)|\1|' | sort -u | grep -v HEAD` # get last commit SHA
  commitTime=`git show --pretty=format:"%ci" | head -1` # get current branch
  version=${curBranch}@${commitID}@${commitTime}
  # echo "${version}" >> dist/version.txt 
  echo "${version}" > ${versionDist} # 不做追加处理,直接重写;
  echo "=====版本记录生产version.txt成功====${version}======"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# git alias

# 配置 git alias提升工作效率

它的基本用法是 git config --global alias.<简化的字符> 原始命令; 命令行配置;

$ git config --global alias.co checkout
$ git config --global alias.ci commit
$ git config --global alias.br branch
1
2
3

这里将 co表示checkoutci表示commitbr 表示branch, 以后提交就可以简写成;

直接修改全局文件配置:vim ~/.gitconfig

[alias]
st = status -sb
co = checkout
br = branch
mg = merge
ci = commit
ds = diff --staged
dt = difftool
mt = mergetool
last = log -1 HEAD
latest = for-each-ref --sort=-committerdate --format=\"%(committername)@%(refname:short) [%(committerdate:short)] %(contents)\"
ls = log --pretty=format:\"%C(yellow)%h %C(blue)%ad %C(red)%d %C(reset)%s %C(green)[%cn]\" --decorate --date=short
hist = log --pretty=format:\"%C(yellow)%h %C(red)%d %C(reset)%s %C(green)[%an] %C(blue)%ad\" --topo-order --graph --date=short
type = cat-file -t
dump = cat-file -p
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

每次想查看 git 的历史记录,就不用输入那么一长串命令 直接使用 git lg

# 命令操作整理

# 一、新建代码库

# 在当前目录新建一个Git代码库
$ git init

# 新建一个目录,将其初始化为Git代码库
$ git init [project-name]

# 下载一个项目和它的整个代码历史
$ git clone [url]

git clone --depth=1 [url]

# 可视化命令;查看仓库的所有历史提交记录
git log --graph --oneline --all

# git clone --depth=1 总结:大大加快克隆的速度
# 好处是限制clone的深度,不会下载协作的历史记录,为1即表示只克隆最近一次commit; 可用在构建或者模板简单实用时;
# 适合用场景:只是想clone最新版本来使用或学习,而不是参与整个项目的开发工作; 

git clone --depth 1  --branch dev https://github.com/samyzh/git-starter.git

#这种方法克隆的项目只包含最近的一次commit的一个分支,体积很小,即可解决项目过大导致Timeout的问题,
#但会产生另外一个问题,只会把默认分支clone下来,其他远程分支并不在本地,所以这种情况下,需要用如下方法拉取其他分支:
$ git clone --depth 1 https://github.com/samyzh/git-starter.git
$ git remote set-branches origin 'remote_branch_name'
$ git fetch --depth 1 origin remote_branch_name
$ git checkout remote_branch_name
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 二、配置

# 显示当前的Git配置
$ git config --list

# 编辑Git配置文件
$ git config -e [--global]

# 设置提交代码时的用户信息
$ git config [--global] user.name "[name]"
$ git config [--global] user.email "[email address]"

git config --list #全部
git config --global  --list#全局
git config --local  --list#本地
1
2
3
4
5
6
7
8
9
10
11
12
13

# 三、增加/删除文件

# 添加指定文件到暂存区
$ git add [file1] [file2] ...

# 添加指定目录到暂存区,包括子目录
$ git add [dir]

# 添加当前目录的所有文件到暂存区
$ git add .

# 添加每个变化前,都会要求确认
# 对于同一个文件的多处变化,可以实现分次提交
$ git add -p

# 删除工作区文件,并且将这次删除放入暂存区
$ git rm [file1] [file2] ...

# 停止追踪指定文件,但该文件会保留在工作区
$ git rm --cached [file]

# 改名文件,并且将这个改名放入暂存区
$ git mv [file-original] [file-renamed]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 四、代码提交

# 提交暂存区到仓库区
$ git commit -m [message]

# 提交暂存区的指定文件到仓库区
$ git commit [file1] [file2] ... -m [message]

# 提交工作区自上次commit之后的变化,直接到仓库区
$ git commit -a

# 提交时显示所有diff信息
$ git commit -v

# 使用一次新的commit,替代上一次提交
# 如果代码没有任何新变化,则用来改写上一次commit的提交信息
$ git commit --amend -m [message]

# 重做上一次commit,并包括指定文件的新变化
$ git commit --amend [file1] [file2] ...

# 绕过提交检测规范
$ git commit --no-verify -m "xxxx" 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 五、分支

# 列出所有本地分支
$ git branch

# 列出所有远程分支
$ git branch -r

# 列出所有本地分支和远程分支
$ git branch -a

# 新建一个分支,但依然停留在当前分支
$ git branch [branch-name]

# 新建一个分支,并切换到该分支
$ git checkout -b [branch]

# 新建一个分支,指向指定commit
$ git branch [branch] [commit]

# 新建一个分支,与指定的远程分支建立追踪关系
$ git branch --track [branch] [remote-branch]

# 切换到指定分支,并更新工作区
$ git checkout [branch-name]

# 切换到上一个分支
$ git checkout -

# 建立追踪关系,在现有分支与指定的远程分支之间
$ git branch --set-upstream [branch] [remote-branch]

# 合并指定分支到当前分支
$ git merge [branch]

# 选择一个commit,合并进当前分支
$ git cherry-pick [commit]

# 删除分支
$ git branch -d [branch-name]

# 删除远程分支
$ git push origin --delete [branch-name]
$ git branch -dr [remote/branch]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 六、标签

# 列出所有tag
$ git tag

# 新建一个tag在当前commit
$ git tag [tag]

# 新建一个tag在指定commit
$ git tag [tag] [commit]

# 删除本地tag
$ git tag -d [tag]

# 删除远程tag
$ git push origin :refs/tags/[tagName]

# 查看tag信息
$ git show [tag]

# 提交指定tag
$ git push [remote] [tag]

# 提交所有tag
$ git push [remote] --tags

# 新建一个分支,指向某个tag
$ git checkout -b [branch] [tag]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 七、查看信息

# 显示有变更的文件
$ git status

# 显示当前分支的版本历史
$ git log

# 显示commit历史,以及每次commit发生变更的文件
$ git log --stat

# 搜索提交历史,根据关键词
$ git log -S [keyword]

# 显示某个commit之后的所有变动,每个commit占据一行
$ git log [tag] HEAD --pretty=format:%s

# 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件
$ git log [tag] HEAD --grep feature

# 显示某个文件的版本历史,包括文件改名
$ git log --follow [file]
$ git whatchanged [file]

# 显示指定文件相关的每一次diff
$ git log -p [file]

# 显示过去5次提交
$ git log -5 --pretty --oneline
git log --graph --pretty=oneline --abbrev-commit

# 显示所有提交过的用户,按提交次数排序
$ git shortlog -sn

# 显示指定文件是什么人在什么时间修改过
$ git blame [file]

# 显示暂存区和工作区的差异
$ git diff

# 显示暂存区和上一个commit的差异
$ git diff --cached [file]

# 显示工作区与当前分支最新commit之间的差异
$ git diff HEAD

# 显示两次提交之间的差异
$ git diff [first-branch]...[second-branch]

# 显示今天你写了多少行代码
$ git diff --shortstat "@{0 day ago}"

# 显示某次提交的元数据和内容变化
$ git show [commit]

# 显示某次提交发生变化的文件
$ git show --name-only [commit]

# 显示某次提交时,某个文件的内容
$ git show [commit]:[filename]

# 显示当前分支的最近几次提交
$ git reflog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

# 八、远程同步

# 下载远程仓库的所有变动
$ git fetch [remote]

# 显示所有远程仓库
$ git remote -v

# 显示某个远程仓库的信息
$ git remote show [remote]

# 增加一个新的远程仓库,并命名
$ git remote add [shortname] [url]

# 取回远程仓库的变化,并与本地分支合并
$ git pull [remote] [branch]

# 上传本地指定分支到远程仓库
$ git push [remote] [branch]
#  git push <远程主机名> <本地分支名>:<远程分支名>

# 强行推送当前分支到远程仓库,即使有冲突
$ git push [remote] --force

# 推送所有分支到远程仓库
$ git push [remote] --all
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 九、撤销/回滚

# 恢复暂存区的指定文件到工作区
$ git checkout [file]

# 恢复某个commit的指定文件到暂存区和工作区
$ git checkout [commit] [file]

# 恢复暂存区的所有文件到工作区
$ git checkout .

# 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
$ git reset [file]

# 重置暂存区与工作区,与上一次commit保持一致
$ git reset --hard

# 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
$ git reset [commit]

# 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致
$ git reset --hard [commit]
# git reset --hard

# 重置当前HEAD为指定commit,但保持暂存区和工作区不变
$ git reset --keep [commit]

# 新建一个commit,用来撤销指定commit
# 后者的所有变化都将被前者抵消,并且应用到当前分支
$ git revert [commit]

# 暂时将未提交的变化移除,稍后再移入
$ git stash
$ git stash pop
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# revert与reset命令的比较

  • revert是放弃指定提交的修改,但是会生成一次新的提交,需要填写提交注释,以前的历史记录都在【推荐】
  • reset是指将HEAD指针指到指定提交,历史记录中不会出现放弃的提交记录

# 十、其他

# 生成一个可供发布的压缩包
$ git archive
1
2

# 问题

# 回退版本的处理

  • 情况一:撤销指定文件到指定版本

    # 查看指定文件的历史版本
    git log <filename>
    # 回滚到指定commitID
    git checkout <commitID> <filename>
    
    1
    2
    3
    4
  • 情况二:删除最后一次远程提交

    #方式一:使用revert
    git revert HEAD
    git push origin master
    #方式二:使用reset
    git reset --hard HEAD^
    git push origin master -f
    
    1
    2
    3
    4
    5
    6
  • 情况三:回滚某次提交

    # 找到要回滚的commitID
    git log
    git revert commitID
    #删除某次提交
    git log --oneline -n5
    
    1
    2
    3
    4
    5

# 工具

  • vscode
  • sourcetree
  • github desktop

# 参考链接

http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html

https://git-scm.com/book/zh/v2/

上次更新: 2022/04/15, 05:41:30
×