基础指令
将修改添加到缓存区:git add -A
将缓存区提交:git commit -m "your comment"
显示当前log:git log
(仅包括当前commit的log,也就是如果回退,则只有回退后的log)
显示所有历史操作的log:git reflog
将缓存区等待git commit 的文件回退到git add 之前的状态git reset .
查看当前git add后的文件,不显示untrack(没有git add 的文件)git status -uno
git clone [ssh_url]
将远程仓库同步到本地一份
分支操作
从当前版本创建并切换到另一个版本:git checkout -b [new branch name]
查看当前分支名:git branch
查看不同分支哈希: git log --format=format:"%H"
(format部分可以自定义)
版本回退
硬回退
- 哈希回退
通过git log获取每次提交的hash值,使用git reset --hard [hash]
直接回退到指定的版本 - 数字回退
使用git reset --hard HEAD~n
(n值为前n次版本) - 符号回退
使用git reset --hard HEAD^
(’'符号的数量表示前n次,eg:^为前2次)
注意
硬回退会清空当前的所有未提交的add和commit,回滚前最好commit一次。
软回滚
使用git reset --soft [hash]
,除了hash回退,其他索引方式和hard类似
硬回滚和软回滚的区别
软回滚只是将当前HEAD值回退,内容是不变的,回退后使用git status
会发现有一些需要add,commit的新内容。hard就不会,hard后status是干净的。当然soft也类似于后悔药。软回滚前没有commit的内容是能够找回的。
放弃修改
一般有如下几种方式,但都只对文件中的修改有效,对于增加的文件是没有效果的
- 使用checkout
git checkout .
,返回值Updated x paths from the index
,只要x不为0,之前的修改基本就放弃了 - reset
先使用git log
获取最近的提交
然后使用commit 5b316fc9ecc802d6da8c165cf89b339b2ecd5ad3 (HEAD -> master) Author: root <root@ThinkingCalculu.localdomain> Date: Sun May 16 23:30:09 2021 +0800 diff_add commit 5f5f9cb40cd2c3ffecdebe6bf2554652bfee13f0 Author: root <root@ThinkingCalculu.localdomain> Date: Sun May 16 23:23:28 2021 +0800 m2 commit 945c47b2f5c5893e52c4d70aeef25029640a1aa6 Author: root <root@ThinkingCalculu.localdomain> Date: Sun May 16 23:22:53 2021 +0800
git reset --hard [git_hash]
的方式来reset即可,其中的git_hash值为最后一次commit得到的值,相当于使用硬回滚的方式取消了最后一次commit后的修改。
下tag
tag类似于里程碑,下tag后,可以通过tag号来管理不同版本的修改。
git tag -a [tag_name] -m "tag_message" :下一个tag,这样可以创建一个tag。一般用v1,v1.1,v2...来作为tag。
git tag -d [tag_name] :删除tag号
git show [tag_name] :显示tag的相关信息,包括tag message,commit 信息,甚至是修改的diff
tag实际上是特殊的commit,因为commit不容易表示,使用tag,可以更直观的了解当前的状态,也可以用于快速回滚
git describe :查看当前的tag号
git tag :显示所有的tag号,查到后可以类似于commit id一样直接回退到指定版本,git reset --hard [tag_name]
理所当然可以通过两个tag号抓diff,参考,总之,除了创建外,使用方式上和commit id是类似的。
cherry pick
git cherry-pick [A]
若当前为master,A是dev分支的一次提交的hash值,则上述操作将dev的A的修改添加为master的新分支A1,A1的修改内容和A是一模一样的(除了hash不一样)。此外,若此时git status
,会发现没有任何修改,因为cherry pick是创建一个新节点,获得的新内容(dev-A)默认以原来的信息commit到新分支master上了。
cherry pick 是一次创建新节点的过程,只不过该节点的所增加的文件和commit信息都是来源分支提供的,当然,该节点的hash值还是新的。
这意味着,如果要撤回这次cherry pick,需要使用git reset --hard HEAD^
硬回滚的方式切换回cherry pick的状态。
可以同时cherry pick多个不同分支的不同修改,只要使用git log [branch name]
就能获得指定branch的log,进而获得hash值,而后使用git cherry-pick [A] [B] [C] ....
参考
git fetch && git pull
git fetch 是将远程库拉到本地库中
git pull 是把远程库拉到本地库中并创建一个节点
**区别:**都用于同步代码,但前者留了一线,以免遇到新代码有问题,也就相对更加安全
git blame(Show what revision and author last modified each line of a file)
查看某行的最后一次的修改(这意味着如果改行在最后一次修改前还有其他的改动是无法查出来的,除非一步步reset回去)
git blame -L x,y [filename]
:查看x,y行范围内的修改,输出如下
$ git blame -L 6,7 makefile
e7735dcc (chen 2021-09-25 21:45:04 +0800 6) //fun2()
b2fe2f72 (chen 2021-09-25 21:51:22 +0800 7) //fun3()
该打印输出的是从该行创建开始的每一次修改,当某行自第一次创建以来就没有改动,则在最前面会用```^``标记
^772fc4b (chen 2021-09-25 21:42:32 +0800 1) echo "this is makefile"
^772fc4b (chen 2021-09-25 21:42:32 +0800 2)
e5e26914 (chen 2021-09-25 21:43:58 +0800 3) fun()
e5e26914 (chen 2021-09-25 21:43:58 +0800 4) {
e5e26914 (chen 2021-09-25 21:43:58 +0800 5) echo ”do some fun in here"
dd303294 (chen 2021-09-25 22:28:07 +0800 6) fun2()+fun4()
b2fe2f72 (chen 2021-09-25 21:51:22 +0800 7) //fun3()
b2fe2f72 (chen 2021-09-25 21:51:22 +0800 8) fun4()
e5e26914 (chen 2021-09-25 21:43:58 +0800 9) }
^772fc4b (chen 2021-09-25 21:42:32 +0800 10)
^772fc4b (chen 2021-09-25 21:42:32 +0800 11)
^772fc4b (chen 2021-09-25 21:42:32 +0800 12)
请注意,如上的有改动的记录,只是显示改行的最后一次改动的相关信息,如dd303294只是相当于当前对6行有改动的记录,如果要查更久之前,要手动reset到dd303294再使用git blame,这在某些情况(前一个作者仅仅修改了该行格式)下显得十分伞兵,当然,如果只是要忽略类似于空格这样的修改的对比结果,可以使用-w参数来实现目的。eg:git blame -w -L 5,8 makefile
参考
git diff && git patch
生成diff
git diff HEAD > [filename]:将工作区的修改导入filename作为diff
git diff [filename] > [diff_filename]:将filename文件的修改记录为diff文件
git diff [commitIDA] [commitIDB] > [diff_filename] 将B相对于A的修改写入diff 文件,A和B间可以是多比修改,会把相应的修改都记录,如果B是在A之前的,则diff的文件记录的是反过来的(如果按照前面的指令的话)
当工作区只有一个file时,两者是相同的
生成patch
git format-patch [commitID] -1
:生成commitID这笔改动涉及的信息
git format-patch [commitID] -n
:生成多个path文件,对应commitID之前的不同修改。如n=2,则生成两个patch文件,一个是commitID这笔修改的信息,另一个是commitID前一笔commitID的修改信息。所指的修改都是相对于前一次commitID。
git format-patch [commitIDA]..[commitIDB]
:生成A到B间修改的patch,包括B的修改,不包括A这笔改动的修改。如果在commit顺序上A在B后面,则不会生成patch文件。
git format-patch [commitID]
:生成ID后的修改patch文件,patch同样不包括commitID这笔改动的修改
注意,不同commitid间的修改有几个就会生成几个文件,生成的patch文件都是基于间隔commit的patch,也就是间隔了几个就生成几个,不会全导入一个文件中
使用diff&patch
检查
git apply --check [diff_file/patch_file]
:检测diff/patch合并会不会冲突,冲突会报
error: patch failed: test:1
error: test: patch does not apply
没有冲突则无显示
合并
git apply [diff_file/patch_file]
误删文件
误删.git/index
结果:
index文件管理缓冲区记录,删除后不会影响实际修改文件,但是会使git失去跟踪状态
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: test
Untracked files:
(use "git add <file>..." to include in what will be committed)
test
当git追踪的文件很多时,上面的列表输出会有很多,删除index后可以使用git restore --staged <file>...
的方式回退deleted之前的状态,但是文件有很多的情况下,使用以下方式会更方便。
git ls-tree -r master --name-only
:显示之前追踪的文件
git reset HEAD [ 被删除的文件或文件夹 ]
:恢复跟踪状态
配合管道符可以变成
git ls-tree -r master --name-only|xargs git reset HEAD
运行该指令后就可以恢复删除index之前的状态了
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: test
repo
repo init -u [option] 抓初始化资源
repo sync 同步资源
效果类似于git clone
个人思考
公司大多数项目都比较大,往往涉及多个模块,在管理上都是各个小模块一个仓库,然后用一个repo把所有的项目包含在一块。
在使用上,repo构建的方式实际上和git差不多,只不过是同时管理多个模块。
repo这种方式本是google 用来管理android 项目的,但在使用的过程中发现其他项目也可以构建自己的repo。
可惜国内除了能够直接获取repo文件外,都是怎么操作抓code,没有搭建的指南。
Repo介绍
日常使用上,可以在.repo/mainfest里面找到目标模块的git 路径,cd到改路径后,使用checkout 到自己要的branch就相当于直接使用该模块的git 管理了,稍微想了下,github上应该是不支持这种多仓库直接抓的才是,看到的初始化地址要么是gerrit的,要么是谷歌的,所以应该是各自公司自己搭建来用的。
常用指令
repo init -u <URL>[option] -b [branchname] 抓初始化资源
repo sync 同步资源
以上指令用于初始化一个repo项目,url 需要自己提供,一般是特指的一个大型管理项目(eg:android)
个人使用上,常用的就是
repo forall [repo=list] -c "[git cmd]"
这样可以快速地管理repo下的多个仓库。
eg:repo forall frameworks/base frameworks/base -c "git status"
,相当于对frameworks/base,frameworks/base这两个项目同时下了git status,注意到不同项目间使用空格隔开,如果不使用repo=list,则默认遍历repo管理的所有项目,在使用上也较为类似,之后看看怎么自己搭一个repo项目