环境搭建 - Git
GIT作为分布式版本控制系统,在现代软件开发中扮演着至关重要的角色。本文将介绍如何搭建GIT开发环境,并探讨其在项目开发中的重要性以及一些技巧。通过正确配置和善用GIT,开发者能够轻松跟踪代码变更、协同工作、回滚修改,并且更好地管理项目的代码版本。了解GIT的基本原理和运用方法,将为开发人员提供更高效、可靠的开发流程,提升团队协作和代码质量。
安装
Windows
只需要到官网下载安装即可, https://git-scm.com/download/win.
Linux
从源码编译 Git 允许你安装最新的 Git 版本,并且自定义一些构建选项。 不管怎么样,这样你就无法通过yum
包管理器来维护你的 Git 安装过程了。
在 CentOS 上开始安装一些构建 Git 的必要依赖软件包
sudo yum groupinstall "Development Tools"
sudo yum install curl-devel expat-devel gettext-devel openssl-devel perl-CPAN perl-devel zlib-devel
通过log版本检测是否安装成功:
git --version
>> git version 2.25.1
设置
查看config配置信息
git与npm一样具体多个位置的配置文件. 即:system级别, global(用户级别) 和local(当前仓库)三个。 配置级别的优先级是:Local > Global > System, 这意味着如果在本地级别进行了配置,它将覆盖全局和系统级别的相同配置。
#查看系统
config git config --system --list
#查看当前用户(global)配置
git config --global --list
#查看当前仓库配置信息
git config --local --list
更新Email/User
git config --global user.name "myname"
git config --global user.email "test@gmail.com"
示例
一次简单的Git操作包括克隆,添加与提交更改,再到摘取与推送:
git clone <远程仓库URL>
git add <文件名>
git commit -m "提交消息"
git pull origin <分支名>
git push origin <分支名>
其中, 我们可查看上次提交后是否有修改 git status
, 一般配合 git status
使用 git diff
查看详细改动.
git add是将快照内容写入缓存区, 它有三个参数:
- git add -A表示添加所有内容
- git add . 表示添加新文件和编辑过的文件不包括删除的文件
- git add -u 表示添加编辑或者删除的文件,不包括新添加的文件
执行 git commit
将缓存区内容添加到本地仓库中, 使用 -m 选择提交注释 git commit
, 将对应分支推送到远程仓库 git push
.
应用
代理
为使用 http/https 协议的 git 配置代理
git config --global http.proxy 'socks5://127.0.0.1:9999'
git config --global https.proxy 'socks5://127.0.0.1:9999'
删除代理
git config --global --unset http.proxy
git config --global --unset https.proxy
npm config delete proxy
SSH config
为 ssh 客户端或者使用 ssh 协议的 git 配置代理 在你用户目录下,建立 .ssh/config,在里面添加如下配置:
# 将这里的 User、Hostname、Port 替换成你需要用 ssh 登录的服务器的配置。
# Host 可以认为像是书签一样的东西,当你用 Host 指明的字符串代替你服务器的 IP/域名 时,
# 便会应用该节点下的配置。当然你也可以将 Host 和 Hostname 设置成一样。
Host yourserver.com
User someone
Hostname yourserver.com
Port 22
Proxycommand /usr/bin/ncat --proxy 127.0.0.1:9999 --proxy-type socks5 %h %p
# 如果是给某同性社交网站用的(走 ssh 协议),可以直接使用该配置。
# 其它类似网站的话,替换掉域名( Host/Hostname)即可。
# 可以看出,ssh 协议的 git 客户端,配置与 ssh 一模一样。
# 需要注意的是这里的 User 应该是 git,而不是你在该网站上注册的用户名。
# (虽然有些提供 git 仓库托管的网站会用其它用户名,这种情况根据网站配置。)
Host github.com
User git
Hostname github.com
Port 22
Proxycommand /usr/bin/ncat --proxy 127.0.0.1:9999 --proxy-type socks5 %h %p
命令分类
根据功能和用途,我们可以将Git命令分为以下五个主要类别
基本操作:
- git add:将文件或更改添加到暂存区。
- git commit:将暂存区的更改提交到版本库。
- git status:查看工作区和暂存区的状态。
- git rm:从工作区和版本库中删除文件。
- git init:初始化一个新的Git仓库。
分支与合并:
- git branch:管理分支,包括创建、列出和删除分支。
- git merge:将一个分支的更改合并到另一个分支。
- git stash:将当前的工作进度保存到一个新的存储栈中。
- git cherry-pick:选择一个或多个提交并将其应用到当前分支。
远程操作:
- git pull:从远程仓库获取最新的更改并合并到本地仓库。
- git push:将本地的更改推送到远程仓库。
- git clone:克隆远程仓库到本地。
- git remote:管理与远程仓库的连接。
查询与比较:
- git log:查看提交日志。
- git diff:比较文件或提交之间的差异。
- git tag:标记特定的提交。
- git fetch:从远程仓库获取最新的提交,但不合并到本地分支。
配置与管理:
- git reset:重置当前分支到指定的提交。
- git config:配置Git的全局或仓库级别的设置。
- git remote:管理与远程仓库的连接。
- git rebase:将一个分支的更改应用到另一个分支。
常用技巧
获取远程tag
git fetch --tags -f
刷新分支
git remote update origin --prune
删除远程不存在的分支
git fetch --prune origin #带fetch后的prune
git remote prune origin # 仅prune
删除冗余tag
git fetch --prune --prune-tags
下载并跟踪所有分支
for branch in $(git branch --all | grep '^\s*remotes' | egrep --invert-match '(:?HEAD|master)$'); do
git branch --track "${branch##*/}" "$branch"
done
回退
查找历史版本
git log
恢复到历史版本
git reset --hard fae6966548e3ae76cfa7f38a461c438cf75ba965
将改动强制推送
git push -f -u origin master
保存用户名密码
设置记住密码(默认15分钟):
git config --global credential.helper cache git config credential.helper cache
手动设置, 这样就设置一个小时之后失效:
git config credential.helper 'cache --timeout=3600'
长期存储密码:
git config --global credential.helper store git config credential.helper store
增加远程地址的时候带上密码也是可以的(推荐)
http://yourname:password@git.oschina.net/name/project.git
git push 超过100M解决办法
git config http.postBuffer 524288000(500M)
常用操作
创建 | CREATE
$ git clone ssh://user@domain.com/xx.git 克隆远程仓库
$ git init 初始化本地 git 仓库(新建仓库)
修改远程仓库地址
git remote set-url origin [url]
先删除再修改地址
git remote rm origin git remote add origin [url]
本地更改 | LOCAL CHANGES
$ git status 查看当前版本状态(是否修改)
$ git diff 显示所有未添加至 index 的变更
$ git diff HEAD 查看已缓存的与未缓存的所有改动
$ git add <path> 将该文件添加到缓存
$ git commit -m ‘xxx’ 提交
$ git commit --amend -m ‘xxx’ 合并上一次提交(用于反复修改)
$ git commit -am ‘xxx’ 将 add 和 commit 合为一步
提交历史记录 | COMMIT HISTORY
$ git log 显示日志
$ git show <commit> 显示某个提交的详细内容
$ git blame <file> 在每一行显示 commit 号,提交者,最早提交日期
分支机构和标签 | BRANCHES & TAGS
$ git branch 显示本地分支
$ git checkout <branch> 切换分支
$ git branch <new-branch> 新建分支
$ git branch --track <new> <remote> 创建新分支跟踪远程分支
$ git branch -d <branch> 删除本地分支
$ git tag <tag-name> 给当前分支打标签
更新和发布 | UPDATE & PUBLISH
$ git remote -v 列出远程分支详细信息
$ git remote show <remote> 显示某个分支信息
$ git remote add <remote> <url> 添加一个新的远程仓库
$ git fetch <remote> 获取远程分支,但不更新本地分支,另需 merge
$ git pull <remote> <branch> 获取远程分支,并更新本地分支
$ git push <remote> <branch> 推送本地更新到远程分支
$ git push <remote> --delete <branch> 删除一个远程分支
$ git push --tags 推送本地标签
合并与衍合 | MERGE & REBASE
$ git merge <branch> 合并分支到当前分支,存在两个
$ git rebase <branch> 合并分支到当前分支,存在一个
$ git rebase --abort 回到执行 rebase 之前
$ git rebase --continue 解决矛盾后继续执行 rebase
$ git mergetool 使用 mergetool 解决冲突
$ git add <resolve-file> 使用冲突文件解决冲突
$ git rm <resolved-file>
撤消 | UNDO
$ git reset --hard HEAD 将当前版本重置为 HEAD(用于 merge 失败) (改过的代码就没)
$ git reset --hard <commit> 将当前版本重置至某一个提交状态(慎用!)
$ git reset <commit> 将当前版本重置至某一个提交状态,代码不变
$ git reset --merge <commit> 重置至某一状态,保留版本库中不同的文件
$ git reset --keep <commit> 重置至某一状态,重置变化的文件,代码改变
$ git checkout HEAD <file> 丢弃本地更改信息并将其存入特定文件
$ git revert <commit> 撤消提交
$ git reset --mixed (版本库和暂存区都将进行了代码回退,工作区代码没动,保留修改)
帮助 | HELP帮助 | HELP
$ git help <command> #获取命令行上的帮助
Rebase
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] [<upstream> [<branch>]]
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git rebase --continue | --skip | --abort | --quit | --edit-todo | --show-current-patch
Rebase参数解析:
--onto <newbase>
: newbase指的是需要rebase代码的起点,newbase可以是分支(branch),也可以是任意的提交记录(commit1)。rebase成功后,将以newbase为代码分支起点。- <upstream>: 需要与newbase比对的分支或提交记录。
- <branch>:需要rebase的工作分支。默认为HEAD,例如dev1
--continue
: 当处理完冲突后,可以使用该命令让rebase继续。--abort
: 终止rebase操作,将HEAD设置为rebase之前的分支。如果指定了<branch>,则HEAD将重置为<branch>。否则,HEAD将被重置为rebase之前的分支。--quit
: 终止rebase操作,但不会将HEAD重置为原来的分支。index和working tree将不会发生任何改变。--autostash
: 在rebase操作之前自动stash当前分支未提交的代码,在rebase操作结束后,自动将stash的记录进行unstash,并与rebase后的代码合并。注意,此操作有可能会导致严重的冲突(conflict),谨慎使用。--merge
: 进行rebase操作的时候,同时使用merge进行代码合并。需要注意的是,在进行rebase merge产生冲突的时候,ours为<upstream>指定的分支,theirs为<branch>指定的分支。原因是,rebase merge work的工作原理为,将<branch>上的所有提交记录在<upstream>分支上进行重提交。
例子:
git checkout dev git rebase origin git rebase --continue
rebase/merge区别
git rebase和git merge是Git中用于合并分支的两种不同方法,它们具有一些区别:
Commit历史记录:
- git merge会创建一个新的合并提交,将两个分支的历史记录保留下来,形成一个新的合并节点。
- git rebase会将当前分支的提交应用到目标分支上,重新生成一系列新的提交,形成一个线性的提交历史。
分支合并视图:
- git merge会保留分支合并的结构,即保留两个分支的独立性,生成一个合并的提交节点。
- git rebase会将当前分支的提交移动到目标分支之后,使得分支合并看起来更加线性,没有合并提交节点。
历史记录的清晰度:
- git merge会保留分支的完整历史记录,每个分支的提交都会被保留下来,保持了分支之间的关系清晰可见。
- git rebase会将当前分支的提交重新应用到目标分支上,可以使提交历史更加简洁、清晰,但可能会丢失一些分支的详细历史信息。
冲突解决:
- git merge在合并过程中,如果存在冲突,会生成一个合并冲突的状态,需要手动解决冲突并进行提交。
- git rebase在应用每个提交时,如果存在冲突,需要解决冲突并使用git rebase --continue命令继续应用提交。
选择使用git merge还是git rebase取决于项目和个人偏好。通常,使用git merge更适合保留分支独立性和清晰的历史记录,而使用git rebase则更适合创建线性的提交历史和简洁的合并视图。然而,需要注意的是,git rebase应该避免在公共分支上进行,因为它会改变提交历史,可能导致其他开发者的困惑和冲突。
保存凭据
使用以下命令来保存凭据(用户名和密码)
git config --global credential.helper store
当你首次使用git push或git pull等命令时,Git会提示你输入用户名和密码,并将它们保存在本地的凭据存储中。以后的操作将自动使用保存的凭据进行身份验证。另外可手动设置缓存时间, 单位timeout为秒
git config credential.helper 'cache --timeout=<timeout>'
不带merge的Pull
git pull --rebase
Git merge --no-ff
在许多介绍 Git 工作流的文章里,都会推荐在合并分支时,加上 --no-ff 参数:
$ git checkout develop $ git merge --no-ff feature
--no-ff 在这的作用是禁止快进式合并。
Git 合并两个分支时,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合并两者时,只会简单地把指针右移,叫做“快进”(fast-forward),比如下图
A---B---C feature / D---E---F master
要把 feature 合并到 master 中,执行以下命令
$ git checkout master $ git merge feature
结果就会变成
A---B---C feature / master D---E---F
因为 feature 就在 master 的下游,所以直接移动了 master 的指针,master 和 feature 都指向了 C。而如果执行了 git merge --no-ff feature 的话,是下面的结果:
A---B---C feature / \ D---E---F-----------G master
由于 --no-ff 禁止了快进,所以会生成一个新的提交,master 指向 G。
从合并后的代码来看,结果其实是一样的,区别就在于 --no-ff 会让 Git 生成一个新的提交对象。为什么要这样?通常我们把 master 作为主分支,上面存放的都是比较稳定的代码,提交频率也很低,而 feature 是用来开发特性的,上面会存在许多零碎的提交,快进式合并会把 feature 的提交历史混入到 master 中,搅乱 master 的提交历史。所以如果你根本不在意提交历史,也不爱管 master 干不干净,那么 --no-ff 其实没什么用。不过,如果某一次 master 出现了问题,你需要回退到上个版本的时候,比如上例,你就会发现退一个版本到了 B,而不是想要的 F,因为 feature 的历史合并进了 master 里。
交互式变基合并
查看提交历史 git log
要知道自己想合并的是哪几个提交,可以使用git log命令来查看提交历史,(历史记录是按照时间排序的,时间近的排在前面) , 假如最近4条历史如下:
commit 3ca6ec340edc66df13423f36f52919dfa3......
commit 1b4056686d1b494a5c86757f9eaed844......
commit 53f244ac8730d33b353bee3b24210b07......
commit 3a4226b4a0b6fa68783b07f1cee7b688.......
git rebase
想要合并1-3条有两个方法
1.从HEAD版本开始往过去数3个版本
git rebase -i HEAD~3
2.指名要合并的版本之前的版本号
git rebase -i 3a4226b
请注意3a4226b这个版本是不参与合并的,可以把它当做一个坐标
选取要合并的提交
1.执行了rebase命令之后,会弹出一个窗口,头几行如下:
pick 3ca6ec3 '注释**********' pick 1b40566 '注释*********' pick 53f244a '注释**********'
2.将pick改为squash或者s,之后保存并关闭文本编辑窗口即可。改完之后文本内容如下:
pick 3ca6ec3 '注释**********' s 1b40566 '注释*********' s 53f244a '注释**********'
3.然后保存退出,Git会压缩提交历史,如果有冲突,需要修改,修改的时候要注意,保留最新的历史,不然我们的修改就丢弃了。修改以后要记得敲下面的命令:
git add . git rebase --continue
如果你想放弃这次压缩的话,执行以下命令:
git rebase --abort
4.如果没有冲突,或者冲突已经解决,则会出现如下的编辑窗口:
# This is a combination of 4 commits. #The first commit’s message is: 注释...... # The 2nd commit’s message is: 注释...... # The 3rd commit’s message is: 注释...... # Please enter the commit message for your changes. Lines starting # with ‘#’ will be ignored, and an empty message aborts the commit.
5.输入wq保存并推出, 再次输入git log查看 commit 历史信息,你会发现这两个 commit 已经合并了。
git 遴选(cherry-pick)
git cherry-pick 命令,也叫 遴选 或者 摘取,目的是把一个分支里面的某次提交,合并到另一个分支里面。
以TortoiseGit为例,叫摘取。
比如说有两个分支:1 分支和 2分支。
我在1分支上,修改了内容,备注为“1测试”,然后提交并推送到远端了。之后又有很多次的提交。
但是我只想把“1测试” 这个提交,合并到 2分支上,要怎么操作呢。
- 先切换到2分支,拉取最新代码。
- 右击 -> 显示日志
- 在日志信息界面,点左上角的分支名称,切换到 remotes/origin/1分支 ,双击进入
- 在日志信息界面上,选择 备注为 “1测试” 的那次提交记录
- 右击->摘取此提交,进入摘取界面,选中记录,点继续,则合并到2分支中了。
- 推送2分支到远端
Git的开发模式
Trunk-based开发和Git Flow是两种常见的代码版本管理和分支管理策略,它们之间存在一些区别:
分支模型:
- Trunk-based开发:Trunk-based开发采用主干(trunk)为主要开发分支,所有开发者在同一个主干上进行工作。一般情况下,只有一个主要的长期分支(通常是主分支或者开发分支)。
- Git Flow:Git Flow采用分支模型,包括主分支(master)和开发分支(develop),以及用于特性开发、修复bug、发布版本等的各种辅助分支。
分支生命周期:
- Trunk-based开发:Trunk-based开发中的特性分支(feature branch)通常只存在很短的时间,一旦开发完成并通过测试,就会尽快合并到主干中。
- Git Flow:Git Flow中的特性分支和其他类型的分支可以存在较长时间,不断进行开发、测试和合并。每个分支都有明确定义的目的和生命周期。
上线频率:
- Trunk-based开发:Trunk-based开发鼓励频繁的集成和部署,使得新功能和修复能够快速上线。
- Git Flow:Git Flow通常倾向于较长的发布周期,通过发布分支(release branch)和预发布环境进行稳定性测试,以确保代码的可靠性和稳定性。
管理复杂性:
- Trunk-based开发:Trunk-based开发简化了分支管理,减少了分支间的冲突和合并问题,适用于较小规模的团队或者较简单的项目。
- Git Flow:Git Flow提供了更多的分支和工作流选项,适用于较大规模的团队或者复杂的项目,可以更好地管理多个功能并行开发、版本发布等。
总体而言,Trunk-based开发适合追求快速交付、持续集成和部署的团队,适用于小型和敏捷的项目。Git Flow适合有较长的发布周期、稳定性和版本管理要求的团队,适用于较大规模和复杂的项目。选择适合自己团队和项目需求的开发模型是关键,也可以根据具体情况结合使用两种策略的元素。
资料
https://docs.github.com/zh