几个值得了解的 Git 操作
前言
记录使用 Git 时一些可能不常用,但是值得了解的功能和命令。
patch 功能
patch
这个词有很多意思,在 Git 当中或者说在计算机中常常译为 补丁。
补丁是指衣服、被褥上为遮掩破洞而钉补上的小布块。现在也指对于大型软件系统(如微软操作系统)在使用过程中暴露的问题(一般由黑客或病毒设计者发现)而发布的解决问题的小程序。就像衣服烂了就要打补丁一样,人编写程序不可能十全十美的,所以软件也免不了会出现BUG,而补丁是专门修复这些BUG做的因为原来发布的软件存在缺陷,发现之后另外编制一个小程序使其完善,这种小程序俗称补丁
gif diff
可能天天在用,其实使用 git diff
的结果再延伸一下,就可以有别的用途了。
个人关于对这个功能的了解,还有一段小插曲。
某日由于疫情突发居家办公,但是笔记本恰好没有带回家,遂用家里的电脑开始办公。打开电脑忽然想起前一日写的代码没有提交(主要是功能没写完,想着写完了一起提交)。让公司同事帮忙提交后又发现有冲突,解起来比较棘手。随一想自己需要的只是个人在本地的改动,即本地的 diff 。只要通过任何方式能获取到本地所有的变更即可,暂时和代码仓库里其他的提交没有关系。gif diff 不就可以输出代码变更吗?那么如何将这些变更快速的转移到自己家里的电脑上,并且直接使用呢?在摸索的过程中就发现了 git patch。
那么 patch
功能到底怎么用呢?下面用一个简单的 demo 来说明一下。简单起见,我们用一个文本文件实例一下(就不用代码了,意思都一样的)
现有 notes.txt 文件,已经进行了一次 commit ,我们对其进行一些修改,具体细节如下。
notes.txt init commit | notes.txt 修改后 |
---|---|
![]() | ![]() |
此时我们使用 git diff > diff.patch
命令可以看到 diff.patch 生成了如下内容。
diff --git a/notes.txt b/notes.txt
index c5aab4b..7d65678 100644
--- a/notes.txt
+++ b/notes.txt
@@ -1,5 +1,5 @@
book
-music
-art
moive
sport
+food
+sleep
这部分内容很很熟系了,平时用 git diff
命令查看文件变更时都见过,git 以行为单位,记录文件的变更,-
+
都可以理解为某一行代码发生变化,可能是删除了,也可能是完全新增,或者是这一行做了修改(修改也可以理解为删了一行,又新增了一行)。
有了 diff.patch
文件,我们就可以脱离远程仓库实现两台物理机代码变更同步了,分布式 yyds 😁😁😁😁 。
我们在另一个同源的仓库,就可以把这个 patch 打上去了。
git apply diff.patch
~/Desktop/test/1111/demo ‹master*› $ cat notes.txt
book
music
art
moive
sport
~/Desktop/test/1111/demo ‹master*› $ git apply diff.patch
~/Desktop/test/1111/demo ‹master*› $ cat notes.txt
book
moive
sport
food
sleep
~/Desktop/test/1111/demo ‹master*›
可以看到 patch 已经生效了。
这样,在公司的同事只用生成一个 patch, 把这个 patch 文件发给我,这样就简单多了。而我呢就可以接着之前的成果继续搬砖了。
patch 功能小结
- 上面虽然是用单个文件做例子,但其实多个文件也是可以的,都可以输出到单个 *.patch 文件中。甚至可以基于自身的需求,某几个文件的 diff 生成一个 patch,然后将这个 patch 打到合适的地方。
- patch 本质上是代码的变更记录;因此,除了基于 git work-space 的变化生成 patch ,我们也可以基于历史 commit 生成 patch。
- git diff 是基于 git track 过的文件生成 diff,因此首次新增的文件,最起码要 add 过之后才会有 diff 产生。当然,git diff 本身就很灵活了,可以基于当前 work-space 的变更或者 git HEAD~N 历史提交输出 diff ,更多用法可以查看文末的参考文档。
- patch 功能只能用于文本文件,无法对资源类文件(比如图片、音视频资源)生效。虽然 patch 可以记录资源文件的变化,但是 apply 是会失败的。想想也不太可能嘛,用一个简单的 patch 文件记录两个视频文件的差异,技术上有可能,但是貌似没有实际意义。
git apply patch
和常用的git cherry-pick
非常相似。但是侧重点不同,patch 更多时候是和第三方合作开发的场景。比如 Android 底层一些硬件驱动的变化、bug 的修复。一般都是由专门的厂商将修复以 patch 的方式给到使用方,使用方将这个 patch 打上就可以了。可以看到做了哪些变化,但也仅此而已。cherry-pick
的使用场景就比较灵活了,任何需要某个特定 commit 的场景,都可以用。有时候被 git merge 搞得代码一片混乱的时候,cherry-pick 不失为解决问题的一种好办法。- git 有专门的命令可以生成更加丰富的 patch ,可以查看文末的参考文档。
这里只是恰好使用 git patch 实现了两台物理机的代码更变同步。实际上关于本地代码不通过远程仓库在两台物理机同步的解决方案太多了,这里只是顺便了解一下 patch 的使用。
shortlog
这个命令就比较好玩了,可以参考 git 仓库一些有意思的信息。
这里以大名鼎鼎的 okhttp 代码库做演示。
- 查看 git 仓库有哪些人做过 commit
在 okhttp 仓库下执行以下命令,便可以看到输出
git shortlog -sn
2082 Jesse Wilson
773 Jake Wharton
594 jwilson
586 Yuri Schimke
231 Adrian Cole
78 renovate[bot]
73 Amir Livneh
56 Neil Fuller
55 Dave Roberge
54 Goooler
30 Marcelo Cortes
25 limpbizkit@gmail.com
19 Eric Cochran
18 Shaishav Gandhi
16 Benoit Quenaudon
15 Masaru Nomura
14 Benoît Quenaudon
12 Tobias Thierer
11 jessewilson@google.com
11 Patrick Forhan
11 Egor Andreevich
10 Narayan Kamath
10 lingming.yb
9 TangHuaiZhe
9 monkey-mas
9 edenman
更多内容,点击查看
7 Zac Sweers
6 Artem Zinnatullin
6 Christian Becker
6 Tang HuaiZhe
5 Steve Lhomme
5 Michael Benedict
5 ericaschulz
5 Kristopher Wuollett
4 Anton Rutkevich
4 John Carlson
4 cketti
4 Jeremy Tecson
4 Egor Andreevici
3 Isaac Green
3 Erik Ghonyan
3 Rob Bygrave
3 RayFromSquare
3 Andre_601
3 Andrew (Paradi) Alexander
3 Roberto Tyley
3 swankjesse
3 Thz
3 Alex Klyubin
2 Björn Kautler
2 Alok Menghrajani
2 Andreas Ahlenstorf
2 Andrew Shu
2 Anton Rieder
2 Carter Kozak
2 Cédric Luthi
2 David Phillips
2 David Robertson
2 Eric Edens
2 Felipe Lima
2 Francisco Montiel
2 Galder Zamarreño
2 Grégory Joseph
2 Jared Burrows
2 Jeff Gilfelt
2 Jochen Schalanda
2 Jung KyungHo
2 Kartikaya Gupta (kats)
2 Lorenzo Colitti
2 Megatron King
2 Michael Parker
2 Niklas Baudy
2 Noriyoshi Fukuzaki
2 Rainer Burgstaller
2 Ras Kasa Williams
2 Richard Thai
2 Roman Tsirulnikov
2 Said Tahsin Dane
2 SatoShun
2 Simon Marquis
2 Stefan M
2 Venil Noronha
2 Warren Smith
2 Yuanjing Zhang
2 boxme
2 shellhub
1 Frieder Bluemle
1 Galder Zamarreño
1 Gaëtan Muller
1 Georgi Staykov
1 Giovanni Longatto Nazario Marques
1 Gonçalo Silva
1 Colton Loewen
1 longbai
1 Guillaume Legrand
1 Guillermo Rodríguez
1 HearSilent
1 Herman Liang
1 Hugo Gresse
1 Ignasi Barrera
1 Igor Fedorenko
1 Clément Dessoude
1 Jairus Mendoza
1 Christopher Orr
1 James Tucker
1 marcinbak
1 Jason Holmes
1 Jasper Vandemalle
1 Jay Estrella
1 Jay Newstrom
1 Jaye Pitzeruse
1 Jeff Bornemann
1 mattlogan
1 Jemar Jones
1 Christopher Kocel
1 Christoph Sitter
1 Jim Hurne
1 mic-
1 Johan von Forstner
1 Christian Brunotte
1 John Rodriguez
1 Jon Ander Peñalba
1 Jon Watson
1 Jonathan Halterman
1 Jonathan Leitschuh
1 Jonathan Steele
1 Jonh Wendell
1 Jordan Stewart
1 Joshua Shanks
1 mike castleman
1 Juti Noppornpitak
1 Kai Zhu
1 Adam Howard
1 Kasra Bigdeli
1 Kazuhiro Sera
1 Kenny Root
1 Kevin Robatel
1 Kiran + Kenji
1 Kirill Boyarshinov
1 Krishna Chaitanya M
1 Chris Cunniff
1 Lavong Soysavanh
1 Liam Newman
1 Lin Wang
1 mruddy
1 Lucas Albuquerque
1 Lukas Aichbauer
1 Maciej Górski
1 Manish Sharma
1 Manuel Jiménez Torres
1 Marc Reichelt
1 Chad Brubaker
1 Marcin Zajączkowski
1 Mark Hobson
1 Markéta Lisová
1 Marvin Ramin
1 Brian Odisho
1 Matt Sheppard
1 Matthew Zavislak
1 Maxim Shafirov
1 nithasha-samad
1 Michael Arenzon
1 Michael Basil
1 Brennan Taylor
1 Michael Evans
1 nmittler
1 Miguel Lavigne
1 (no author)
1 Nash Lincoln
1 Baldur Gudbjornsson
1 Nguyễn Triết Khang
1 Nicklas Ansman Giertz
1 Nicola Corti
1 Nicolás Lichtmaier
1 nxtstep
1 olg
1 Norman Maurer
1 Oliver
1 Ollie Snowden
1 PY
1 Austyn Mahoney
1 Patrick Strawderman
1 Paul Fulham
1 Paul Knudsen
1 Paul Woitaschek
1 Pavel Chuchuva
1 Phil Glass
1 Philip Jagielski
1 Prashant Jois
1 Quinn Neumiiller
1 panmingwei
1 Ralph Bergmann
1 raskasa
1 Atticus White
1 Reinhard Naegele
1 Reinhard Nägele
1 rattanshiv12
1 Ricki Hirner
1 Rob
1 Artem Gavrilov
1 Arlo Breault
1 rbates
1 Russell
1 Ryan O'Meara
1 Aakash Goenka
1 Saket Narayan
1 Santiago Castro
1 rupertbates
1 Sean C. Sullivan
1 Sebastian Kürten
1 Sebastian Schuberth
1 Sergey Galkin
1 Serj Lotutovici
1 Andy Dennie
1 shaunkawano
1 Simon Wimmesberger
1 Skeiro
1 Stanislav Gromov
1 靳阳
1 Stephen Barlow
1 Andrew Gaul
1 Stuart Neivandt
1 Amir Jalal
1 Amin Cheloh
1 TangHuaizhe
1 Tao.Zang
1 Taylor H. Perkins
1 Teresa Krause
1 Thomas Keller
1 Thomas Wirth
1 Alexey Zakharov
1 Tobias T
1 Alex Wegener
1 Tolriq
1 Tom Reznik
1 Tomasz Rozbicki
1 Tony Robalik
1 Tyler Barber
1 Usmann Khan
1 AJ
1 Volodymyr Buberenko
1 Voytovich, Mike
1 utsavoza
1 Wesley Tarle
1 When Sunset
1 WhiskeyBravo
1 Yahia Allam
1 Yang Chen
1 Ye Lin Aung
1 vlad doster
1 Alex Szlavik
1 Alex
1 aahlenst
1 abeggs
1 ahulyk
1 anderius@gmail.com
1 austynmahoney
1 based2
1 bdc@google.com
1 南宫雪珊
1 chhsiao90
1 circlespainter
1 Ahmed El-Helw
1 congwu.wang
1 dc
1 Adrian Pascu
1 edenman@gmail.com
1 Adrian Cole and Josh Humphries and Scott Blum
1 gkimbwala
1 iamdanfox
1 ittzik
1 Adrian Cole and Jesse Wilson
1 jjshanks
1 Danstahrg
1 Daniel Tashjian
1 Adam Taft
1 lburgazzoli
1 Dimitris+Jake
1 Dino Dai Zovi
1 Dino Kovač
1 Dmitry Timofeev
1 Dmitry Zolotukhin
1 DracoBlue
1 Dustin Kut Moy Cheung
1 Eddú Meléndez Gonzales
1 Daniel Cohen Gindi
1 Dan Sănduleac
1 Eitan Adler
1 Elvis de Freitas
1 Emilian Bold
1 Emre
1 Craig Andrews
1 Eric Denman
1 len
1 Eric Gribkoff
1 Eric Kuck
1 Craig
1 Everett Toews
1 Faucogney Anthony
1 Adam Stroud
1 Florent Ramière
1 lingming
1 Adam Speakman
有没有找到自己的名字或者是认识的大佬 ,罒ω罒 。只要提过 PR 并且合入也算是 commitor ,所以可以看到很多 commit 次数为 1 的人。
通过 git shortlog -sn
可以显示当前仓库的所有提交者,并且会按提交 commit 的数量进行降序排列。(当然,这个 commit 数量并不能说明什么,但是可以简单了解一下这个仓库有哪些人做过贡献)。
使用 git shortlog -sne
还可以显示邮箱。 git shortlog 本质上是对 git log 输出的内容进行格式化处理。关于 shortlog 更多的用法可以通过 --help 命令查看。
如果你真的看过 git shortlog --help
的帮助文档,其实还可以发现其他一些有意思的命令行。
- 获取最新一次 commit 的 commit-id
git rev-parse --short HEAD // 返回 commit-id 前 7 位
// or
git rev-parse HEAD // 返回完整的 commit-id
ps:这里其实有个有意思的问题,为什么这个 hash 值前 7 位就可以替代这个 hash 值?
- 获取最新一次 commit 的 branch
git rev-parse --abbrev-ref HEAD
- 获取最新一次 commit 的 author_name
git log -1 --pretty=format:'%an'
- 获取最新一次 commit 的 author_email
git log -1 --pretty=format:'%ae'
当然了,这些信息也可以通过其他方式获取,甚至现在各类 IDE 对 git commit history 的支持都非常完善了,可以直接查看了。
git blame
通过 git blame 可以快速查看某个文件的提交历史。
git blame filename
会按行显示提交记录
添加 -L 参数可以指定行。
git blame -L startline,endline filename
特定行数的提交记录
当然,这个功能 Github 本身就是支持的。
git tag
在平日使用 git 的时候,大家更习惯的是使用分支 branch 。在 git 中,如果说 branch 是随着 commit 越来越多不断延伸的一条河流的话,那么每创建一个 tag 就是在这条河上面建造一座桥。
从任意 git commit 结点创建分支 branch 之后,可以就此分支做 commit , merge 等操作,是动态的;从这个层面出发,git tag 就是静态的。
因此,git tag 在日常开发中使用较少。甚至在某些开发团队,git tag 的创建由专人负责(一般是版本迭代负责人)。 git tag 一般是在一些特殊的、里程碑式的提交结点创建。比如在 App 封版的提交,SDK 特定版本的提交。
git tag 的使用也比较简单。
- 创建 tag
git tag tag_name
- 查看所有 tag
git tag
我们可以看一下 okhttp 的 tag.
git tag --sort=-creatordate
parent-5.0.0-alpha.10
parent-5.0.0-alpha.9
parent-4.10.0
parent-5.0.0-alpha.8
parent-5.0.0-alpha.7
parent-5.0.0-alpha.6
parent-5.0.0-alpha.5
parent-5.0.0-alpha.4
parent-5.0.0-alpha.3
parent-4.9.3
parent-4.9.2
- 删除 tag
git tag -d tag_name
需要注意的是,创建 tag 之后,并不会在 work-space 有记录。因为 tag 可以理解为是某个 commit 的一个影子。其本质上并没有生成新的东西。tag 也不会随 commit 的 push 自动带到远程仓库中,tag 需要手动进行 push
git push <remote> <tag_name>:推送某个标签到远程仓库。
git push <remote> --tags:推送所有标签到远程仓库。
关于 git tag 的更多使用说明,可以查看文末参考文档。
git reflog
- 恢复已经被 reset 或删除的 commit 记录
git reflog
git reflog 很多时候都是 git 误操作的后悔药。
- 不小心删除的 commit 记录,
- merge 代码时误操作干掉的 commit ,
- git reset --hard 之后又想找回来的提交记录,
- 被删除的分支找不到 commit-id 了,无法恢复时
统统可以通过 git reflog 找回来。
git reflog 显示的内容本质上是 git commit 的在本地的所有操作记录。因此,并不能百分之百保证所有提交记录可以被恢复,因此 git 有定期进行 commit 自动清理的策略
因此,不能过度依赖 git reflog, 对仓库进行随意的操作。
希望你的程序员生涯不必用到这个命令来救火。😄😄😄
to be continued
参考文档
转载自:https://juejin.cn/post/7144705696142589960