
代码版本控制对于我们嵌入式软件开发岗是一项基础、必备的技能,需要熟练掌握。实际工作中常用的版本控制系统有:Git(分布式版本控制系统)与SVN(集中式版本控制系统)。 本次分享Git在实际工作中的基本使用方法。 一、Git的下载、安装及配置1、Git与Github:
![]() 官网下载速度稍慢。 方法二(镜像网站下载):![]() 镜像网站下载速度较快。 (2)安装:下载完成后按默认选项安装即可。安装完成在任意文件夹下右击就可以看到: ![]() Git GUI是一个Git图形化客户端软件,Git Bash是一个命令行终端。两个都可以用,根据自己喜好,用其中一即可。 但还是建议用Git Bash,熟练了Git Bash在选用一些图形化客户端也可以(不仅限于Git GUI)。不过话又说回来,熟练了Git Bash之后,好像也没必要再用图形化客户端了。 3、本地git配置任意文件夹下右键进入Git Bash,然后输入如下命令进行本地配置: $ git config --global user.name "user"0 m T" u6 n$ Y3 K$ b$ git config --global user.email "email" 其中, user为你的用户名(随意取),email为你的邮箱,设置这两个信息的原因是在于远程库进行关联时需要一个key,配置这两个信息以确保你的唯一性。 使用--global参数表明你这台机器上所有的仓库都进行相同的配置。比如我的配置: ![]() 前面也提到了,常用的远程仓库有GitHub与Gitee。关联远程仓库的方法都一样,这里我们以Gitee为例。 (1)注册Gitee/GitHub![]() 输入命令: ssh-keygen -t rsa -C "email"创建ssh key。此时,用户根目录下的.ssh/目录下会生成两个文件:id_rsa和id_rsa.pub,把id_rsa.pub文件中的内容复制到gitee账户中进行设置。 输入命令cat ~/.ssh/id_rsa.pub可查看id_rsa.pub的内容。如: ![]() 然后把输出的内容复制一份,待会要用。 (3)在远程仓库中设置ssh key在Gitee的设置中找到SSH公钥一栏: ![]() ![]() 并把刚才我们复制的ssh key粘贴到公钥输入框中: ![]() 其中,标题是随意起的。这是Gitee设置ssh key的方法,Github设置ssh key也是类似的。 二、本地、远程仓库建立关联例如,我们本地有一个git_test工程: ![]() ![]() 下面我们来演示把这个工程上传至远程仓库并建立关联。在进行演示之前我们需要看一个图: ![]() 这个图是使用git来进行版本控制最核心的图。 上行: 首先,使用add命令把工作目录中的文件添加到暂存区中;然后,再使用commit命令提交到本地仓库中。最后,再使用push命令推送到远程仓库。 下行: 第一次需要使用clone命令从远程仓库中克隆一份至本地;之后若是远程仓库有内容更新,可以使用pull拉取新更新的内容至本地。 (1)本地代码管理即使我们不关联远程仓库,我们也可以在本地对自己的代码进行一个管理。以上面的git_test工程为例,首先使用git init命令初始化git_test仓库: ![]() ![]() 输入git init后会在当前工程目录下生成一个 .git/的隐藏目录, 里面有一些相关文件:
正因为有了.git/这个目录,我们才能进行版本控制。.git/里面的内容不再展开说明,有兴趣的朋友可自行研究。 把git_test工程下的文件添加到暂存区: ![]() 其中: git status命令:查看状态。/ S- x5 Y' |9 N' W4 Zgit add <file>:添加文件至暂存区。 git add .:将工作区所有变化提交到暂存区。 将暂存区文件提交到本地仓库: ![]() 首先,在Gitee上创建一个名为git_test的仓库: ![]() ![]() 创建完仓库后,仓库页面会有一些简单的说明及命令,可以重点关注一下。因为上一小节我们已经创建好了本地仓库 ,所以按照已有仓库的操作即可: ![]() 第一次上传需要输入gitee的账号密码。输入账号密码之后即可完成上传: ![]() 其中, 第一条命令是把本地库与远程库进行关联,其中远程仓库的名字origin是可以更改的;第二条命令是把本地仓库的内容推送到远程仓库中。 我们的本地仓库既可以同步到Gitee,也可以同时同步到GitHub。同步到GitHub上的方法与同步到Gitee上是一样的。 需要注意的是这里远程仓库的名字不能是origin,因为上面我们的GitHub远程软件仓库的名字已经命名为origin。 此时,刷新远程仓库可看到我们上传的文件: ![]() 至此,我们的本地仓库与远程仓库已经建立起了连接,就可以很好地进行版本管理了。下面依旧通过实例来演示一下使用git的一些操作。 三、Git常用操作进入公司第一天,老大给你分配一个git账号及一个远程仓库地址。假如这个远程仓库的地址是:
你首先要做的就是配置git,然后你可以创建一个文件夹,专门放需要管理的代码工程。假如这个这个文件夹是git_project,我们把上面远程仓库的项目克隆一份到git_project下: ![]() 这也是我们上面上传的工程。我们添加一个README.md文件并上传至远程仓库: ![]() ![]() ![]() 因为第一天上班,所以你下班下得很早。你的同事更改了代码并上传,上传的代码如: ![]() 第二天你上班,先拉取最新代码至本地,执行命令: git pull origin master![]() 然后就可以愉快地编写我们的代码了。执行拉取操作时有可能会碰到类似这样的情况:我们本地修改了module1.c文件,假如修改为: ![]() 而远程仓库上的module1.c也被更新了,假如更新为: ![]() 此时如果执行拉取操作,会覆盖掉本地的module1.c文件吗,会覆盖掉你的代码的?不会的,因为如果有冲突的话拉取就不成功,需要解决冲突才可重新拉取。 比如此时我们进行拉取操作,则产生冲突: ![]() 此时我们可以执行git stash命令把我们的本地修改临时保存,然后再执行拉取操作,最后执行git stash pop命令恢复我们的暂存: ![]() 问题又来了,刚才临时保存的会覆盖掉刚刚拉取的代码吗?不会的。保留或者丢弃都是由我们进行选择的,比如: ![]() 我们可以保留双方修改,也可以采用当前修改(刚从远程仓库拉去的代码),也可以采用传入的更改(临时保存恢复的代码),还可以比较变更: ![]() 这里我们选择保留双方修改,代码变成: ![]() 以上就是最基本最常用的git操作了,即怎么管理代码、怎么拉取代码、怎么上传代码。 除此之外,还有些常用命令及操作大家可以遇到的时候再查看怎么用,比如,如何创建分支、合并分支、版本回退等等内容。由于篇幅过长,不再展开进行演示。 四、主要注意事项1、刚开始使用git时,若是不熟练,在push代码或者pull代码之前可以做一下备份,防止出了不可逆转的代码丢失。 2、提交代码前务必工程编译通过,并且没有修改别人的代码。可以使用对比软件(如beyond compare)对比对比自己做了哪些修改,是不是都是必要的修改,有哪些是要丢弃的。 五、一些Git图形化客户端及工具用命令行还是用图形化客户端看个人习惯,这里也分享一些工具(来自 码云 Gitee 推荐):
讲得很详细,还配有短视频演示。 2、Learn Git Branching这是一个可以在线学习Git命令的网站: ![]() ![]() 以下命令来自: 1、新建代码库# 在当前目录新建一个Git代码库( W/ a3 S3 w1 T- g8 W $ git init 0 `) ~' W8 v& Q5 U5 \& }6 _: Y% C # 新建一个目录,将其初始化为Git代码库 $ git init [project-name]$ d! O- L# A: ^% U4 d 1 |# Y' ], N! Q& m # 下载一个项目和它的整个代码历史 X1 b7 i( t$ q O) |/ m+ D $ git clone [url] 2、配置 Git的设置文件为.gitconfig,它可以在用户主目录下(全局配置),也可以在项目目录下(项目配置)。 # 显示当前的Git配置$ git config --list # 编辑Git配置文件 $ git config -e [--global]3 M; {) Z: U5 v) i/ D4 L2 `: G 4 F. N* C4 o# @' ?- x/ | # 设置提交代码时的用户信息 $ git config [--global] user.name "[name]" $ git config [--global] user.email "[email address]" 3、增加/删除文件# 添加指定文件到暂存区 $ git add [file1] [file2] ...- y2 t( ~- E7 S5 d7 M" U. J; v+ j # 添加指定目录到暂存区,包括子目录. O. O, v3 R; m. v& D $ git add [dir]1 m, P, j1 M8 J% ^7 C # 添加当前目录的所有文件到暂存区/ g0 M5 `8 x( M) `# S $ git add . 2 `% x3 m# @% G( r # 添加每个变化前,都会要求确认 # 对于同一个文件的多处变化,可以实现分次提交2 \, j' K' `& ~" e) `2 _: ^ $ git add -p' u& z0 g: U4 l/ ^/ l% C5 l # 删除工作区文件,并且将这次删除放入暂存区, t, v8 A/ p% L $ git rm [file1] [file2] ... # 停止追踪指定文件,但该文件会保留在工作区 $ git rm --cached [file] # 改名文件,并且将这个改名放入暂存区8 X4 p; D7 R- p; w $ git mv [file-original] [file-renamed]2 r; l1 Q9 U( g 4、代码提交# 提交暂存区到仓库区 $ git commit -m [message], V% B2 u) F: y3 A! A0 a v # 提交暂存区的指定文件到仓库区 $ git commit [file1] [file2] ... -m [message] # 提交工作区自上次commit之后的变化,直接到仓库区 $ git commit -a # 提交时显示所有diff信息! x; R1 \5 e8 |; a $ git commit -v! J" H4 a" H1 p9 z' U! J + ^; {- l+ `6 [ J* \- J # 使用一次新的commit,替代上一次提交 # 如果代码没有任何新变化,则用来改写上一次commit的提交信息 $ git commit --amend -m [message] - v4 B2 |8 Z. m6 D # 重做上一次commit,并包括指定文件的新变化+ s A4 b: \$ y$ S, E E' } $ git commit --amend [file1] [file2] ...! b4 j( ]- J( k5 U7 H! t/ P9 s 5、分支# 列出所有本地分支 $ git branch8 w$ M) b0 B: E - v0 P v. B: \- r! ? # 列出所有远程分支 $ git branch -r # 列出所有本地分支和远程分支/ Y5 }1 C3 V, U, F. p' A$ ?! l8 R6 S $ git branch -a% R3 L' ^. _9 v% X8 x; g" G' r 3 I$ B! {$ }) c, A/ m # 新建一个分支,但依然停留在当前分支 $ git branch [branch-name]5 Z, i" ?0 @$ a# }& g+ B / k$ ]7 J* i9 l. u2 y0 s5 } # 新建一个分支,并切换到该分支, c- z! X/ i1 G( Z. {7 I& R $ git checkout -b [branch]1 q& J9 I! F/ y( U # 新建一个分支,指向指定commit $ git branch [branch] [commit] O8 {$ V* c O! N5 F: L & d! w. h& P/ x2 o, R9 V% _ # 新建一个分支,与指定的远程分支建立追踪关系 $ git branch --track [branch] [remote-branch] 3 a+ O, C, y/ r4 h) f0 S% G # 切换到指定分支,并更新工作区7 J3 R" u: A* s3 W' T( f% t9 M& B $ git checkout [branch-name] 5 X6 P7 O% q" e% |: Y& `6 R( X6 _ # 切换到上一个分支; L) Y. o7 M& n4 w1 n $ git checkout - # 建立追踪关系,在现有分支与指定的远程分支之间. S% \7 a1 J1 i, o: r" |4 [2 p $ git branch --set-upstream [branch] [remote-branch] # 合并指定分支到当前分支1 _. {* n* P+ l- ~0 {% f $ git merge [branch]8 x- w! q% B2 N) W& t6 m/ n # 选择一个commit,合并进当前分支! X7 N b/ @" j0 i0 _0 R- _ $ git cherry-pick [commit]1 I* H, B! z$ b0 N) S; g ' V- V8 ^5 ?: ^; A4 C # 删除分支 $ git branch -d [branch-name] ) X! [2 K, i- W( q. T # 删除远程分支& o- B" p. a4 | ?( V. U $ git push origin --delete [branch-name] $ git branch -dr [remote/branch] 6、标签# 列出所有tag/ w! L1 S7 V$ n" Y" B $ git tag # 新建一个tag在当前commit $ git tag [tag] % s( D! f C9 a& I # 新建一个tag在指定commit, d% D2 d& J% A. t1 h" v $ git tag [tag] [commit] 7 {3 x! O) C8 t/ c4 F9 l # 删除本地tag( w0 p$ F6 B; j $ git tag -d [tag]) n2 m, x: D: B$ K/ |' {; j # 删除远程tag $ git push origin :refs/tags/[tagName] & S# U1 H3 M* k4 S2 v9 T # 查看tag信息" }9 @$ d" J3 j3 h2 n$ L $ git show [tag] 3 w6 Q6 v( t6 n5 x! J5 Z # 提交指定tag $ git push [remote] [tag]) J/ z5 T' b# [- y& }- |. s # 提交所有tag6 }# e# \/ t3 [7 x3 r7 } $ git push [remote] --tags# p+ t! O. ~2 A1 h: a . g( Z' N+ M1 _2 v) K' d # 新建一个分支,指向某个tag $ git checkout -b [branch] [tag] 7、查看信息# 显示有变更的文件2 b& V4 w. A& a $ git status : @! n, y0 O8 X+ i( p9 ] m7 G # 显示当前分支的版本历史 $ git log5 ]; E& u2 `# v ]4 u # 显示commit历史,以及每次commit发生变更的文件+ T$ g) }3 Q- ~% ~/ V3 m $ git log --stat 5 }8 ^; Z- L" k+ N. y. U/ q # 搜索提交历史,根据关键词; K0 a; m7 c* `- K1 ?+ ~% G- t/ { $ git log -S [keyword]+ {" s1 c" b2 n4 m) ] 7 \, A& m" Q* [6 _) w, v # 显示某个commit之后的所有变动,每个commit占据一行 $ git log [tag] HEAD --pretty=format:%s " r" W5 C: i* c5 N" b% e5 t # 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件 $ git log [tag] HEAD --grep feature* E" {: P H9 O # 显示某个文件的版本历史,包括文件改名 $ git log --follow [file] $ git whatchanged [file]! r' S+ Z2 x. L # 显示指定文件相关的每一次diff $ git log -p [file] # 显示过去5次提交! H/ f; g: [( C: T. b& s8 C: P $ git log -5 --pretty --oneline 7 ?2 @% D, s* g0 n6 W) ^ # 显示所有提交过的用户,按提交次数排序0 Q' i" E( L' ]( J- K! g& ]; x $ git shortlog -sn7 _' `6 D2 _6 e* ? 2 x! S" u$ A( u: e7 t # 显示指定文件是什么人在什么时间修改过/ _* b( W" m1 h0 Y $ git blame [file] # 显示暂存区和工作区的差异# q; O# i& U% H# ? d $ git diff) i) Y, [& ^1 S+ l # 显示暂存区和上一个commit的差异 $ git diff --cached [file] - @2 A+ k" G3 k! w$ p # 显示工作区与当前分支最新commit之间的差异 $ git diff HEAD ( x7 y- m# R: K3 t # 显示两次提交之间的差异 $ git diff [first-branch]...[second-branch]( U" w$ J Y0 D # 显示今天你写了多少行代码6 |, U& j. `& E5 Q' h" \5 m" B& q $ git diff --shortstat "@{0 day ago}"0 f. o4 L' b9 T4 C / c" n. W0 k& F/ d9 P5 r/ h( v # 显示某次提交的元数据和内容变化% ]* `+ T0 }, D0 K" k% t $ git show [commit] # 显示某次提交发生变化的文件- e' j9 S1 m' Z/ G$ a1 I, N $ git show --name-only [commit]# f. ~8 k' Y. e* v/ w # 显示某次提交时,某个文件的内容 $ git show [commit]:[filename] + g2 W0 L4 j; Y. n' C # 显示当前分支的最近几次提交7 g, ]# f. ?3 \! O: e2 f $ git reflog& ~3 x; n" S8 j$ g& X/ ` 8、远程同步# 下载远程仓库的所有变动9 E5 I$ C( k: [& b" y$ u4 z $ git fetch [remote]5 U1 S! \0 e, R% O$ E8 m& | ; l. b9 F# [0 T, ^ # 显示所有远程仓库 $ git remote -v # 显示某个远程仓库的信息 $ git remote show [remote] # 增加一个新的远程仓库,并命名: {' A# l j- L $ git remote add [shortname] [url]1 @0 \) H7 @$ K7 R- l3 `. i& r& M & T; t% \9 \) E1 J; X u # 取回远程仓库的变化,并与本地分支合并2 k: ]& k6 U+ K $ git pull [remote] [branch] # k9 |7 N& @5 g7 u% U3 j/ r # 上传本地指定分支到远程仓库& a$ a7 J+ g2 z( _! a) f $ git push [remote] [branch]8 r" X0 x9 `( H2 X8 B # 强行推送当前分支到远程仓库,即使有冲突6 c: N4 R8 P1 ^) [* a! i $ git push [remote] --force . Q* g. j7 a) L: Y0 F2 g% g5 k # 推送所有分支到远程仓库 $ git push [remote] --all8 `& G+ B( o- B2 Z) q 9、撤销# 恢复暂存区的指定文件到工作区 I8 p7 H* _) F% O# ^ $ git checkout [file] # 恢复某个commit的指定文件到暂存区和工作区 $ git checkout [commit] [file]6 f8 u8 B' T1 N/ k # |3 @6 I5 d& ?5 I # 恢复暂存区的所有文件到工作区 $ git checkout . # 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变 $ git reset [file]2 p1 _/ C. c- E( F* R9 l 5 G* \+ O( L2 Z } # 重置暂存区与工作区,与上一次commit保持一致$ j! l+ m% C- ` $ git reset --hard # 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变 $ git reset [commit]3 B( h6 X! _+ l : H: Y* T o2 \- U9 U# R # 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致/ U3 @0 h, j" n, n5 G8 ? $ git reset --hard [commit]+ s# ]9 D8 Y+ f$ {0 f % `/ }/ C. j, N1 x5 H8 ? # 重置当前HEAD为指定commit,但保持暂存区和工作区不变+ e; \6 H2 [5 m, q; o% s $ git reset --keep [commit]% {4 F5 D8 j0 h! q . g2 Y2 T6 Q/ H" q, e+ g # 新建一个commit,用来撤销指定commit( [ d& z& L# n8 ~" j' J2 H # 后者的所有变化都将被前者抵消,并且应用到当前分支 $ git revert [commit] # 暂时将未提交的变化移除,稍后再移入8 t/ e" ^7 D ]( A $ git stash $ git stash pop8 }9 A" h$ ~5 a+ C: H, g 10、其他# 生成一个可供发布的压缩包 $ git archive' u# ]0 D4 {, w" L* U3 H. S% i |
感謝![]() |
学习一下 |