[日常办公][code manage]git cheatsheet

堵睿范
2023-12-01

GIT

基本使用

配置用户名邮箱

$ git config -l #list config
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com"
$ git config -–unset -–global user.name #unset config

本地创建仓库

$ mkdir demo_repo && cd demo_repo
$ git init

添加文件

$ git add readme.txt
$ git add dummy1.txt
$ git add dummy2.txt
$ git add -p #choose hunks to be added to patch

提交到仓库

$ git commit -m "commit 1 readme file, 2 dummy file"
$ git commit -s #add signature
$ git commit --author #change author

工作区,暂存区(stage), 分支commit(快照)历史

$ git add    #工作区文件添加到暂存区
$ git add -p #add hunks
$ git commit #暂存区文件提交commit

查看仓库状态,当前分支,文件修改等

$ git status
$ git status -uno # untrack files are no, no status

查看具体修改内容

$ git diff readme.txt 
$ git diff --stat # get statics
$ git diff HEAD -- readme.txt #比较工作区和最新commit 
$ git diff -- file
$ git diff --staged or --cached
$ git difftool

版本回退、切换

从近到远显示commit历史, commit id, author,message等等

$ git log #aka git log HEAD
$ git log --pretty=oneline #清爽信息
$ git log --pretty=short --abbrev-commit
$ git log -p
$ git log -- file
$ git log --no-merges #not show merge info
$ git log --follow file # get log of a file

回退

$ git reset --hard HEAD^     #回退到前1个commit
$ git reset --hard HEAD^^    #回退到前2个commit
$ git reset --hard HEAD~100  #回退到前100个commit
$ git reset --hard 1094a     #切换到指定commit id

误删commit, 如何恢复
查看命令记录,找回误删的commit id

$ git reflog
$ git reflog branch
$ git reset --hard xxxx

丢弃工作区文件的修改

$ git checkout -- readme.txt #回退到暂存区的状态
$ git checkout -- readme.txt #如果暂存区干净回退到最新commit的状态

丢弃暂存区的修改

$ git reset HEAD <file>  #回退到最新commit的状态

删除文件

$ rm test.txt
$ git rm test.txt #删除版本库中版本
$ git mv # rename files
$ git commit -m "remove test.txt" #提交删除

误删回退

$ git checkout -- test.txt 

本地分支管理

HEAD指向当前分支,分支指向commit

    HEAD
      \
    master
      \
O--O--O

创建,切换分支

$ git branch dev         #创建分支
$ git checkout dev       #切换分支       $git switch  dev
$ git checkout -b dev    #创建并切换分支 $git switch -c dev
    master
      \
O--O--O
      |
     dev
      | 
    HEAD

查看当前分支

$ git branch
$ git branch -r
$ git branch -a
$ git branch -vv
$ git branch --contains commit

切换分支发生的事

$ git add readme.txt                #在dev分支上 add
$ git commit -m "on branch dev"     #在dev分支上 commit
    master
      |
O--O--O~~O
         |
        dev
         | 
        HEAD
$ git checkout master               #切换回master
    HEAD
      \
    master
      \
O--O--O~~O
         |
        dev

在当前分支上合并分支

Fast-forward合并分支

$ git merge dev     #在master上合并dev
#实际是Fast-forward类型的合并
         HEAD
           \
         master
           \
O--O--O--~~O
           |
          dev

强制禁用 fast forward,会创建新的commit

$ git merge --no-ff -m "merge with no-ff" dev
         HEAD
           \
         master
            \
O--O--O------O
        \   /
         O
         |
        dev

删除分支

$ git branch -d dev
         HEAD
           \
         master
           \
O--O--O--~~O

合并冲突的解决

当前分支状态

         HEAD
           \
         master
            |
            O
           /
O--O--O--O
          \
           O
           |
          feature1

在当前分支(master)

$ git merge feature1
Auto-merging...
CONFLICT (content)
...
#发生了冲突

查看冲突,手动修改

$ git status
手动修改冲突文件
$ vi ...

然后在当前分支

$ git add foo.bar
$ git commit -m "fix conflict"
                 HEAD
                  |  
                 master
                   /
O--O--O--O --O -- O
          \     /
           O---/
           |
          feature1

查看合并情况

$ git log --graph --pretty=oneline --abbrev-commit

删除分支

$ git branch -d feature1

分支数目和用途的设置

廖雪峰给的一个管理策略,未必就是最佳实践
master:应当很稳定,仅用来发布新版本,不用来开发
dev:分支用来开发,发布新版本时候,在master分支合并dev
平时开发:从dev上创建新分支合并。。。

修复bug的流程示例

在dev分支上,接到任务修bug

$ git status #查看当前分支,比如dev
$ git stash  #暂存当前工作区
$ git stash save "your stash message"

加入从master分支着手修复bug

$ git switch master
$ git checkout -b issue-101 #bug分支

在issue-101分支上

$ git add readme.txt 
$ git commit -m "fix bug 101"
[issue-101 4c805e2] fix bug 101

修复完成后,回到master合并修复内容

$ git switch master
$ git merge --no-ff -m "merged bug fix 101" issue-101

回到dev继续干活

$ git switch dev
#查看工作现场
$ git stash list
$ git stash show -p stash@{1}
$ git stash apply #恢复工作区
$ git stash drop  #删除保存的状态
$ git stash pop   #先恢复,然后删除保存的状态
$ git stash branch dev #checkout a dev branch and then stash

cherry-pick “复制”一个commit到当前分支
dev是从master过来的,可能这个bug在dev上也有
cherry-pick只复制commit,不用merge

$ git branch
* dev
  master
$ git cherry-pick 4c805e2  #修bug的commit  [issue-101 4c805e2] fix bug 101
[master 1d4b803] fix bug 101
 1 file changed, 1 insertion(+), 1 deletion(-)

开发新feature的流程示例

从dev创建新分支feature,开发完成后在合并到dev
中间feature取消的话可以强制删除

$ git branch -D <name>

远程仓库及多人协作问题

基本操作

以某种方式创建远程仓库 e.g. github
在本地

$ cd demo_repo
$ git remote -v
$ git remote update/add/remove/rename
$ git ls-remote
$ git branch -u[--set-upstream-to]=origin/master dev
$ git remote add origin git@github.com:username/demo_repo.git
#添加了远程库 名字: origin, 地址 git@,,,.git

推送本地内容至远程库

$ git push -u origin master
$ git push origin HEAD:refs/for/xx/xx/xx

-u参数,
不仅把本地的master分支内容推送的远程新的master分支,
还会把本地的master分支和远程的master分支关联起来,
在以后的推送或者拉取时就可以简化命令

$ git push origin master #关联后的简化命令

克隆远程仓库

$ git clone git@github.com:username/demo_repo.git

多人协作

git clone 远程仓库时,自动把本地master和远程master对应,远程仓库默认为origin
查看远程仓库

$ git remote
$ git remote -v #verbose

推送分支:把本地分支上的commit推送到远程库

$ git push origin master  #指定本地分支master, 推送到远程库的对应分支
$ git push origin dev   #推送本地的dev分支

用户b在a之前推送更新到远程仓库

# user_b执行clone远程仓库
user_b@ubuntu$ git clone git@github.com:username/demo_repo.git
# 默认只有master分支
$ git branch
# clone远程仓库后,在本地使用远程分支
user_b@ubuntu$ git checkout -b dev origin/dev    #在本地创建远程分支
...git add...git commit...
user_b@ubuntu$ git push origin dev               #推送到远程分支

推送更新,以及冲突解决

user_a本地也作了修改,也想push到origin/dev

# 当前在dev分支
user_a@ubuntu$ git push origin dev
# 推送失败,两个推送有冲突,因为当前分支落后了

1)user_a 拉取最新内容

#git pull <remote> <branch> <=> git fetch; git merge
user_a@ubuntu$ git pull 
# 拉取失败,当前的dev分支和origin/dev分支没有指明链接
user_a@ubuntu$ git branch --set-upstream-to=origin/dev dev   #指明链接
user_a@ubuntu$ git pull      #可以拉取
#显示有冲突

2)手动解决冲突的文件
3)然后添加commit,推送

user_a@ubuntu$ git add foo.bar
user_a@ubuntu$ git commit -m "fix conflict"
# 然后推送到dev分支
user_a@ubuntu$ git push origin dev

完成。

rebase

rebase & squash

on master: 
1--2--3
on master:
git checkout -c feature
on master:
		  master
			|
1--2--3--4--5
on feature:
			   feature
			     |
1--2--3~~10~~11~~12

merge

$ git checkout feature
$ git merge master
		  master
			|
1--2--3--4--5----------|
	   \~~10~~11~~12~~20
	   				   |
	   			   feature
  • pros :操作简单熟悉;保存提交历史和时间顺序;保存分支的上下文
  • cons:大量的merge commit会污染提交记录;git bisect困难

rebase

$ git checkout feature
$ git rebase master
		  master
			|
1--2--3--4--5~~10~~11~~12
	   					|
	   			 	feature
$ git rebase -i master # interactive mode
squash 10xxxxx Commit message#1
squash 11e8a9b Commit message#2
# pick
# squash
# fix up
# 清理历史,压缩commit
可能的结果
		  master
			|
1--2--3--4--5~~12
	   			|
	   		feature
  • pros: 简化复杂的提交历史;单个提交容易进行;避免merge的提交污染历史;清处中间commit,有利于DevOps 团队
  • cons: 压缩大量提交会隐藏上下文;团队合作时,rebase 公开repo可能是危险的;需要更多工作,feature branch需要持续更新;rebase远程分支需要force push

The biggest problem people face is they force push but haven’t set git push default. This results in updates to all branches having the same name, both locally and remotely, and that is dreadful to deal with.

如何选择呢? 取决于团队

pr

把开发分支向dev目标分支创建pull request
可以在开发dev的时候,持续开发,持续得到dev和目标分支的比较,可以用做代码review

分支策略

https://docs.microsoft.com/en-us/azure/devops/repos/git/git-branching-guidance?view=azure-devops

标签管理

标签是某分支上某commit的别名

$ git tag  v0.1 #为当前分支的最新commit打tag
$ git tag -a v0.1 -m "version 0.1 released" 1094adb 
#为指定的commit打tag,并加注释

查看tag

$ git tag
$ git show v0.9

推送标签到远程仓库

$ git push origin v1.0	 #推送单个
$ git push origin --tags #推送全部

删除标签

$ git tag -d v0.1                   # 1)先从本地删除
$ git push origin :refs/tags/v0.9   # 2)然后删除远程

打patch

引用链接
patch中存储的是对代码的修改
生成patch就是记录你对代码的修改并将其保存在patch文件中
打patch就是将patch文件中对代码的修改,应用到源代码,从而把对代码的修改应用到code中

diff和patch两个命令可以生成patch和打patch,但是对多个文件不方便;没办法保存commit信息

下面的方法可以以project为单位;可以保存commit信息;灵活,可以获取任意commit之间的patch集

$ git format-patch
$ git format-patch -n
$ git format-patch -n -o path/
$ git format-patch commit1..commit2
$ git am
$ git am -3
$ git format-patch HEAD^
#生成最近的1次commit的patch
$ git format-patch HEAD^^               
#生成最近的2次commit的patch
$ git format-patch <r1>..<r2>
#生成两个commit间的修改的patch(包含两个commit. <r1>和<r2>都是具体的commit号)
$ git format-patch -1 <r1>
#生成单个commit的patch
$ git format-patch <r1>
#生成某commit以来的修改patch(不包含该commit)
$ git format-patch --root <r1>
#生成从根到r1提交的所有patch

git am

$ git apply --stat 0001-limit-log-function.patch
# 查看patch的情况
$ git apply --check 0001-limit-log-function.patch
# 检查patch是否能够打上,如果没有任何输出,则说明无冲突,可以打上
(注:git apply是另外一种打patch的命令,
其与git am的区别是,
git apply并不会将commit message等打上去,
打完patch后需要重新git add和git commit,
而git am会直接将patch的所有信息打上去,
而且不用重新git add和git commit,
author也是patch的author而不是打patch的人)

$ git am 0001-limit-log-function.patch
# 将名字为0001-limit-log-function.patch的patch打上
$ git am --signoff 0001-limit-log-function.patch
# 添加-s或者--signoff,还可以把自己的名字添加为signed off by信息,作用是注明打patch的人是谁,因为有时打patch的人并不是patch的作者
$ git am ~/patch-set/*.patch
# 将路径~/patch-set/*.patch 按照先后顺序打上
$ git am --abort 
# 当git am失败时,用以将已经在am过程中打上的patch废弃掉
#(比如有三个patch,打到第三个patch时有冲突,那么这条命令会把打上的前两个patch丢弃掉,返回没有打patch的状态)
#$ git am --resolved
#当git am失败,解决完冲突后,这条命令会接着打patch

patch冲突的解决

(1) 根据git am失败的信息,找到发生冲突的具体patch文件,
然后用命令git apply --reject <patch_name>,强行打这个patch,
发生冲突的部分会保存为.rej文件
(例如发生冲突的文件是a.txt,那么运行完这个命令后,发生conflict的部分会保存为a.txt.rej),
未发生冲突的部分会成功打上patch
(2) 根据.rej文件,通过编辑该patch文件的方式解决冲突。
(3) 废弃上一条am命令已经打了的patch:git am --abort
(4) 重新打patch:git am ~/patch-set/*.patchpatch

misc

modify current commit

$git commit --amend

clean code

git clean -xn
git clean -xn
git clean -xdf

push to remote

git push -u origin main 
git push origin platform/android/main HEAD
git push HEAD:refs/for/main #gerrit

rebase

https://stackoverflow.com/questions/616556/how-do-you-squash-commits-into-one-patch-with-git-format-patch




REPO

insatll repo on ubuntu 20.04

$apt install gnupg python
  • download python-kerberos from https://packages.ubuntu.com/eoan/python-kerberos
  • download repo from https://packages.ubuntu.com/eoan/repo
sudo dpkg -i python-kerberos # python-kerberos first.
sudo dpkg -i repo # python-kerberos first.

init repo

$repo init -u ssh://the-user-name@xxx/xxx-manifests -m xx/master.xml
$repo sync
$ls -a
proj_name/sub1/
proj_name/sub2/
.repo/

start brach

$cd proj_name/
$repo start abc_dev # create branch for all sub modules

upload commit

$repo upload #upload all
# uncomment two lines for each submodule: a line of branch, a line of commit id 

$cd proj_name/sub1/ #uplaod for current
$repo upload .

record version

#record all git info
repo manifest -r -o manifest.xml 
#get commit id
xx_commit_info=`git rev-parse HEAD`

more info

  • 本文大部分内容是对这篇教程二次整理所得
  • http://www.ruanyifeng.com/blog/2020/04/git-cherry-pick.html
  • https://source.android.com/setup/develop/repo
  • https://source.android.com/setup/contribute/submit-patches

issue

LR CLRF

git config --global core.autocrlf false
 类似资料: