使用Git
Git操作
初始化
这是简单指南,图解git,可以放在收藏夹想不起来了拿起来看看.
装好之后先配置一下账户。
/etc/gitconfig
是系统级别的配置,使用--system,这些配置是会按级别覆盖的,仓库覆盖账户覆盖系统~/.gitconfig
下是账户级别的配置git config --global user.name "ooowl"
git config --global user.email "123456789@qq.com"
作者是谁,仓库commit会有你的名字,名字和email(名字可以重复但email一般唯一)并不作为身份标识,只是一个标记而已所有的独立提交不记名只验证公钥或github账号,你可以通过这样来查看git config --local user.name
git config --local user.email
git init就会在该目录下生成一个.git文件夹,想知道这个文件夹里面是什么就自己去搜。git config --xxxx --list
显示本级下git 的配置
📌Tip
个人习惯git commit的信息类型[add,delete,fix,feature,end]-此功能第几次提交-其他描述
其实这个是有明确规范名的,小项目无所谓,大项目看项目需要,遵守github定义的规范还可以自动生成change logs
命令
不用说的命令
git --version
git status
看看自己目前仓库里未跟踪的文件,修改未提交的文件git clone
克隆项目git add (.)
把文件添加到暂存区,未跟踪的文件可以直接添加为跟踪git pull
git commit -m "提交信息"
git fetch
从更新分支状态
- 首先,Git 会与指定的远程仓库建立通信,并检查是否有新的提交历史。
- Git 查找本地仓库中不存在的远程分支和标签,并将它们的引用(reference)下载到本地。
- 如果有新的提交历史,Git 会将这些提交的数据下载到本地仓库中,但不会自动合并到当前分支。
- 如果有远程分支与本地分支存在对应关系,Git 会更新本地分支的指针(引用)以指向远程分支的最新提交。
- 完成后,你可以运行其他命令(如
git merge
或git rebase
)来将下载的提交合并到当前分支。
不常用的命令
git show (id or tag)
查看这次id和tag的详细信息,包括提交时间作者提交了什么更改git log (--oneline --all --graph )
查看所有的提交记录,回滚需要至少7位id,-p查看提交具体干了啥,--all查看所有的分支,--graph 尽可能用图形表示。git reflog
查看命令历史,以便确定要回到未来的哪个版本git reflog show --date=iso new
查看本分支所有的记录git blame File
查看File的更改提交历史git rebase
不要用,会变基!!!
Warning
merge会在head后面紧接着提交,合并后自动提交一次作为标记,worktree会有一个分支记录。
rebase会在分支根提交点处接着提交,之后再跟上master该节点之后的的提交,worktree不会有分支记录。
你可以认为 HEAD(大写)是"current branch"(当下的分支)。当你用git checkout切换分支的时候,HEAD 修订版本重新指向新的分支。有的时候HEAD会指向一个没有分支名字的修订版本,这种情况叫”detached HEAD“
head(小写)是commit对象的引用,每个head都有一个名字(分支名字或者标签名字等等),但是默认情况下,每个叫master的repository都会有一个head, 一个repository可以包含任意数量的head。在任何时候,只要这个head被选择成为”current head“,那么这个head就成了HEAD,总是大写
管理远程仓库
默认的仓库为origin
git remote set-url origin <remote-url> # 为已有的源设置url
git remote -vv # 查看所有的仓库
git remote add vps ssh://git@addr:port/path/test.git # 添加远程仓库,只会添加到本地而不会影像其他人的git
git remote remove vps
管理分支
git branch # 查看本地分支
git branch -r # 查看远程的分支
git branch -vv # 查看所有的本地分支和远程的对应关系
git checkout -b new [existing] # 从existing创建新的分支,如果不指定那就是当前所处的分支
git branch feature/new-feature # 创建子分支,可以用来归档
git branch -d name # 本地删除name分支
git push origin --delete name # 推送远程删除远程的name分支
git branch -m old_name new_name # 重命名分支
git push -u origin new_name # 将本地的新分支推送到远程
# 此时远程会有两个分支,本地只会有这个重命名的新分支,其他人可以拉新分支,最后删掉旧分支就可以
# 合并分支
git merge abc # 当前所在的分支和指定的分支进行合并
git merge --abort # 合并冲突之后放弃合并
# 追踪分支
git branch --set-upstream-to=origin/main main # 追踪origin/main分支
git branch --unset-upstream main # 当前分支不再追踪main分支
git for-each-ref --format='%(committerdate:short): %(refname:short)' refs/heads # 查看所有的分支的创建时间使用refs/remotes查看远程的
# %(refname):完整的分支名
# %(authordate):分支创建者的提交时间
# %(taggerdate):最近的标签时间
📌Tip
checkout 不仅能切换分支,实际上什么也能切换,分支HEADcommit等都可以
但是switch 是只能切换分支,功能有重合
管理代码
提交什么的我就不说了,暂存会使代码恢复到上一次提交的状态,如果又修改了文件导致冲突,那就需要解决冲突
git diff [文件] # 对比哪些文件被修改了
# 从历史区回退本次commit
git diff --check 分支合并之后检查是否还有没结局的冲突
# 暂存代码
git stash save "暂时保存"
git stash list # 列出所有的暂存
git stash apply stash@{0} # 应用第一个暂存
git stash drop stash@{0} # 删除第一个暂存
git stash pop stash@{0} # 应用第一个暂存并删除
git stash show -p stash@{0} # 查看某存储的文件改动加-p是带内容
git checkout commitid # 回滚到某次commit。可用于在此分支提交新的分支
git push --force # 用当前覆盖远程此分支的所有commit记录,可以用来清理旧的提交,先删除.git文件夹,然后新建分支追踪远程,最后强制推送
git cherry-pick # 复制某个commit到目前的分支现在的状态下(樱桃是坏文明)
git pull --rebase # 先拉取代码,然后再把你的提交放到拉取的最新提交之后,相当于先pull下来再作你的更改提交
git rebase --abort # 如果pull rebase的代码有冲突了,你可以使用这个命令回归到没有pull rebase的状态,就可以正常pull解决冲突合并推送
恢复
git restore file(path)# 恢复文件(某路径的所有文件)到上次提交的状态,丢弃已经更改的!!!
回滚之后又修改,和远程进行merge,直接覆盖远程
git reset --soft HEAD~1 # 我想恢复上次的提交,就是我后悔上次提交,还应该再改改再提交
git reset --hard HEAD~1 # 我想回到上次的提交,但是不保留提交内容
git reset HEAD [file] # 在已经add状态下撤销add,不带文件名就是整个仓库
管理tag
相当于给某一次的commit加标记,标签是唯一的,标签打出来之后可以在github选择进行release[1]
git tag # 列出所有标签
git log --oneline # 也会列出标签
git log -p # 显示每次提交的diff
git tag -l 'V1*' # 列出符合正则表达式的标签
git show [tag_name] # 显示特定标签的细节
git tag -a tag_name [-m 备注信息] [commitID(不指定就是当前的head)] # 给一个commit打标签,可以添加备注信息
git tag -d tag_name # 删除本地的标签
git push origin -d tag 标签1名称 标签n名称 # 删除远程的若干个标签
git push origin tag_1 tag_2 # 推送若干标签到远端
git push origin --tags # 推送所有的标签到远端
git的管理结构
当你切换分支的时候,Git 会用该分支的最后提交的快照替换你的工作目录的内容, 所以多个分支不需要多个目录
HEAD相关的东西
HEAD是指向当前的指针,就是指向任何一个东西都可以。
例如想要从已经提交过的commit历史创建记录没必要回滚回去, 直接把HEAD移过去就可以checkout 当你的当你的HEAD不在最新的分支的时候,You are in 'detached HEAD' state.
,就意味着你的提交和修改都会在一个匿名的分支上,如果你不管它直接切回别的分支,那么此分支会被git标记为可清除,会在适当的时候被git自己的垃圾回收清除
如果要保留那就直接从此处新建分支
Click to see more
jack ~/Desktop/gitdemo main ↑2 git checkout 78a9df1 ✔ 5423 23:27:08
Note: switching to '78a9df1'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Turn off this advice by setting config variable advice.detachedHead to false
HEAD is now at 78a9df1 匿名修
jack ~/Desktop/gitdemo 78a9df1 git checkout 78a9df1 ✔ 5424 23:27:19
HEAD is now at 78a9df1 匿名修
HEAD常用有三种
- HEAD~1 是在当前分支上找父提交,往回退1次
- HEAD^1 是在merge过来的分支找父提交,往回退1次
- HEAD@{1} 是从哪里过来的,就指回哪里去,但是此时也是指针分离状态,小心操作
📌Tip
git中的@{n} n∈[0,+♾️] 从0开始,stash和HEAD中都是
服务器Git repo
客户机生成公钥
申请用户
申请用户并设置密码,申请git组,把用到的用户加到这个组里,一般git就够了
groupadd git
useradd git -g git
在服务器上root进入/etc/ssh
目录,编辑 sshd_config
,打开以下三个配置的注释:
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
/etc/rc.d/init.d/sshd restart //重启ssh
修改权限并创建仓库
在home下建立对应的工程最好,每个用管理自己的工程
sudo git init --bare /home/git/gitdemo.git
mkdir -p /home/git/.ssh
touch /home/git/.ssh/authorized_keys
一定要修改权限!!!
免密登录也是这个权限,本身就是免密登录ssh传送的文件.
如果权限对的话就不用管
Warning
修改 .ssh 目录的权限为 700
修改 .ssh/authorized_keys 文件的权限为 600
仓库 .ssh目录和authorized_keys都要修改所有者
chown -R git:git /home/git
chmod 700 /home/git/.ssh
chmod 600 /home/git/.ssh/authorized_keys
然后将客户机们的公钥id_rsa.pub
里面的内容复制到git用户的authorized_keys
里面
每个用户单独的ssh K,这样就可以一个用户对应一个工程互不影响,多个工程只要申请多个用户,放到git用户组更管理就可以了.
最后设置用户只能使用git禁止登陆ssh,并修改密码,编辑/etc/passwd
git:x:502:504::/home/git:/bin/bash //找到这一行
git:x:502:504::/home/git:/bin/git-shell//改为这个
连接远程分支
这时候客户机本地应该可以clone了,url有两种写法(推荐ssh)
git@192.168.0.100:/home/git/gitdemo.git //SCP写法
ssh://git@192.168.0.100:23456/home/git/gitdemo.git //SSH写法,适用于非22端口
//Https的写法没有配置,而且每次都要输密码,团队协作Ω不好用
如果是本地已经有repo了,可和远程连接起来git remote add origin git@192.168.0.100:/home/git/gitdemo.git
git push --set-upstream origin main
把本地的main分支和远程main关联起来(假设只有main分支)
pull,merge就可以用来了
冷知识,仓库的语言是server设置的,没用的知识增加了。
参考
gitflow工作流
gitflow工作流,团队开发的范例。
github工作流
主要流程为:
- 新建分支(Create a branch)
- 提交修改(Add commits)
- 创建PR(Open a Pull Request)
- 代码评审(Discuss and review your code)
- 部署(Deploy)
- 合并(Merge)
在github提PR
单纯的提PR,不对github这种代码平台和协作的操作记录,如果以后要用到gitlab或者github协作的时候再去看 #TODO
ignore参考
#
此为注释*.a
忽略所有.a结尾的文件!lib.a
但lib.a除外/todo
仅仅忽略项目根目录下的todo文件,不包括subdir/todobuild/*
忽略build/目录下的所有文件doc/*.txt
忽略doc/notes.txt(只忽略doc下本身的文件),但不包括doc/server/arch.txt(子目录下的文件)- 原来已经被跟踪的文件是无效的,必须先取消跟踪git rm --cache,下次提交就不会生效了
.idea/**/*
忽略某个(idea)文件夹下所有的文件和子文件夹(递归忽略所有)
踩坑
多平台换行符问题(LF or CRLF)
[2]
出现的问题git提示code analyze的时候换行符是否要转换
文本文件所使用的换行符,在不同的系统平台上是不一样的。UNIX/Linux 使用的是 0x0A(LF)
,早期的 Mac OS 使用的是 0x0D(CR)
,后来的 OS X 在更换内核后与 UNIX 保持一致了。但 DOS/Windows 一直使用 0x0D0A(CRLF)
作为换行符
在不同平台上,换行符发生改变时,Git 会认为整个文件被修改,这就造成我们没法 diff
,不能正确反映本次的修改。会出现所有文件都冲突的情况
git使用git config --global core.autocrlf true
用于在提交和检出时自动转换换行符,选项有三个
true
提交时转换为LF,检出时转换为CRLFinput
提交时转换为LF,检出时不转换false
提交检出均不转换
git使用 git config --global core.safecrlf true
用于检查文件是否包含混合换行符,该配置也有三个可选项:
true
禁止提交混合换行符的文本文件(git add 的时候会被拦截,提示异常)warn
提交混合换行符的文本文件的时候发出警告,但是不会阻止 git add 操作false
不禁止提交混合换行符的文本文件(默认配置)
解决方法
直接无视然后提交,两个属性都设为false
不转换不警告,这样会造成换行符混乱,diff可能会失效
一种规范换行符的方式是这样的:
使用 Windows 系统的开发者设置:git config --global core.aurocrlf true
使用 Linux/MacOS 的开发者设置:git config --global core.autocrlf input
离线使用git
完全离线但是要更新项目的情况。 在git的服务器服务端的仓库repo.git,复制到离线的服务器,假设放在/dev/repo.git
。
在服务器项目目录先clone一下git clone /dev/repo.git
,如果是一个新的repo,有可能分支不对,克隆完是空的。
进入目录,如果分支对不上且目录是空的,需要手动同步过来。
停止对某文件跟踪
现在有个需求是每个平台的config文件的配置是不一样的,想要从git中将此文件取消跟踪,手动置换机器中的此文件(其实开不一样的分支就行,但是开分支也怪麻烦的)
在全内网的环境下是无法进行git同步的,这种情况下与其复制过去再修改不如新建分支,每次是把main分支的代码往上合,到时候直接把这个分支的代码复制过去就可以跑了
git rm --cached 文件名
停止对git已跟踪的文件跟踪,但保留之前该文件的跟踪状态
在一个分支下开发,这种做法是没有问题的。即在 master 分支里,使用如上操作之后,config.js 就从 git 管理中剔除了,但是仍然存在于硬盘上。并不会影响到本地开发环境的正常调试。
如果同时满足以下条件的情况下就会出问题
1 多分支同时存在 config.php 文件。例如,master 分支,dev 分支都有 config.php 文件
2 一个分支删除了该文件,然后在另一个分支,merge 了该提交
这时候dev和master分支的config.js就全消失了。
git update-index --assume-unchanged 文件名
暂时让git忽略已跟踪的文件,具体看这一篇
虽然能达到(暂时的)目的,但并非最正确的做法,这样做是误解了 git update-index 的含义,而且这样做带来的最直接(不良)后果是这样的:
1 所有的团队成员都必须对目标文件执行:git update-index --assume-unchanged PATH。这是因为即使你让 Git 假装看不见目标文件的改变,但文件本身还是在 Git 的历史记录里的,所以团队的每个人在 fetch 的时候都会拉到目标文件的变更。(但实际上目标文件是根本不想被 Git 记录的,而不是假装看不见它发生了改变)
2 一旦有人改变目标文件之后没有 git update-index --assume-unchanged PATH 就直接 push 了,那么接下来所有拉取了最新代码的成员必须重新执行 update-index,否则 Git 又会开始记录目标文件的变化。这一点实际上很常见的,比如说某成员换了机器或者硬盘,重新 clone 了一份代码库,由于目标文件还在 Git 的历史记录里,所以他/她很可能会忘记 update-index。
git update-index --assume-unchanged 的真正用法是这样的:
1 你正在修改一个巨大的文件,你先对其git update-index --assume-unchanged
,这样 Git 暂时不会理睬你对文件做的修改;
2 当你的工作告一段落决定可以提交的时候,重置改标识:git update-index --no-assume-unchanged
,于是 Git 只需要做一次更新,这是完全可以接受的了;
3 提交+推送。