标签 Git 下的文章

在我们有关 Git 鲜为人知的用法系列的最后一篇文章中,了解如何使用 Git 跟踪项目中的大型多媒体文件。

Git 是专用于源代码版本控制的工具。因此,Git 很少被用于非纯文本的项目以及行业。然而,异步工作流的优点是十分诱人的,尤其是在一些日益增长的行业中,这种类型的行业把重要的计算和重要的艺术创作结合起来,这包括网页设计、视觉效果、视频游戏、出版、货币设计(是的,这是一个真实的行业)、教育……等等。还有许多行业属于这个类型。

在这个 Git 系列文章中,我们分享了六种鲜为人知的 Git 使用方法。在最后一篇文章中,我们将介绍将 Git 的优点带到管理多媒体文件的软件。

Git 管理多媒体文件的问题

众所周知,Git 用于处理非文本文件不是很好,但是这并不妨碍我们进行尝试。下面是一个使用 Git 来复制照片文件的例子:

$ du -hs
108K .
$ cp ~/photos/dandelion.tif .
$ git add dandelion.tif
$ git commit -m 'added a photo'
[master (root-commit) fa6caa7] two photos
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 dandelion.tif
$ du -hs
1.8M .

目前为止没有什么异常。增加一个 1.8MB 的照片到一个目录下,使得目录变成了 1.8 MB 的大小。所以下一步,我们尝试删除文件。

$ git rm dandelion.tif
$ git commit -m 'deleted a photo'
$ du -hs
828K .

在这里我们可以看到有些问题:删除一个已经被提交的文件,还是会使得存储库的大小扩大到原来的 8 倍(从 108K 到 828K)。我们可以测试多次来得到一个更好的平均值,但是这个简单的演示与我的经验一致。提交非文本文件,在一开始花费空间比较少,但是一个工程活跃地时间越长,人们可能对静态内容修改的会更多,更多的零碎文件会被加和到一起。当一个 Git 存储库变的越来越大,主要的成本往往是速度。拉取和推送的时间,从最初抿一口咖啡的时间到你觉得你可能断网了。

静态内容导致 Git 存储库的体积不断扩大的原因是什么呢?那些通过文本的构成的文件,允许 Git 只拉取那些修改的部分。光栅图以及音乐文件对 Git 文件而言与文本不同,你可以查看一下 .png 和 .wav 文件中的二进制数据。所以,Git 只不过是获取了全部的数据,并且创建了一个新的副本,哪怕是一张图仅仅修改了一个像素。

Git-portal

在实践中,许多多媒体项目不需要或者不想追踪媒体的历史记录。相对于文本或者代码的部分,项目的媒体部分一般有一个不同的生命周期。媒体资源一般按一个方向产生:一张图片从铅笔草稿开始,以数字绘画的形式抵达它的目的地。然后,尽管文本能够回滚到早起的版本,但是艺术制品只会一直向前发展。工程中的媒体很少被绑定到一个特定的版本。例外情况通常是反映数据集的图形,通常是可以用基于文本的格式(如 SVG)完成的表、图形或图表。

所以,在许多同时包含文本(无论是叙事散文还是代码)和媒体的工程中,Git 是一个用于文件管理的,可接受的解决方案,只要有一个在版本控制循环之外的游乐场来给艺术家游玩就行。

 title=

一个启用这个特性的简单方法是 Git-portal,这是一个通过带有 Git 钩子的 Bash 脚本,它可将静态文件从文件夹中移出 Git 的范围,并通过符号链接来取代它们。Git 提交链接文件(有时候称作别名或快捷方式),这种符号链接文件比较小,所以所有的提交都是文本文件和那些代表媒体文件的链接。因为替身文件是符号链接,所以工程还会像预期的运行,因为本地机器会处理他们,转换成“真实的”副本。当用符号链接替换出文件时,Git-portal 维护了项目的结构,因此,如果你认为 Git-portal 不适合你的项目,或者你需要构建项目的一个没有符号链接的版本(比如用于分发),则可以轻松地逆转该过程。

Git-portal 也允许通过 rsync 来远程同步静态资源,所以用户可以设置一个远程存储位置,来做为一个中心的授权源。

Git-portal 对于多媒体的工程是一个理想的解决方案。类似的多媒体工程包括视频游戏、桌面游戏、需要进行大型 3D 模型渲染和纹理的虚拟现实工程、带图以及 .odt 输出的书籍、协作型的博客站点、音乐项目,等等。艺术家在应用程序中以图层(在图形世界中)和曲目(在音乐世界中)的形式执行版本控制并不少见——因此,Git 不会向多媒体项目文件本身添加任何内容。Git 的功能可用于艺术项目的其他部分(例如散文和叙述、项目管理、字幕文件、致谢、营销副本、文档等),而结构化远程备份的功能则由艺术家使用。

安装 Git-portal

Git-portal 的 RPM 安装包位于 https://klaatu.fedorapeople.org/git-portal,可用于下载和安装。

此外,用户可以从 Git-portal 的 Gitlab 主页手动安装。这仅仅是一个 Bash 脚本以及一些 Git 钩子(也是 Bash 脚本),但是需要一个快速的构建过程来让它知道安装的位置。

$ git clone https://gitlab.com/slackermedia/git-portal.git git-portal.clone
$ cd git-portal.clone
$ ./configure
$ make
$ sudo make install

使用 Git-portal

Git-portal 与 Git 一起使用。这意味着,如同 Git 的所有大型文件扩展一样,都需要记住一些额外的步骤。但是,你仅仅需要在处理你的媒体资源的时候使用 Git-portal,所以很容易记住,除非你把大文件都当做文本文件来进行处理(对于 Git 用户很少见)。使用 Git-portal 必须做的一个安装步骤是:

$ mkdir bigproject.git
$ cd !$
$ git init
$ git-portal init

Git-portal 的 init 函数在 Git 存储库中创建了一个 _portal 文件夹并且添加到 .gitignore 文件中。

在平日里使用 Git-portal 和 Git 协同十分平滑。一个较好的例子是基于 MIDI 的音乐项目:音乐工作站产生的项目文件是基于文本的,但是 MIDI 文件是二进制数据:

$ ls -1
_portal
song.1.qtr
song.qtr
song-Track_1-1.mid
song-Track_1-3.mid
song-Track_2-1.mid
$ git add song*qtr
$ git-portal song-Track*mid
$ git add song-Track*mid

如果你查看一下 _portal 文件夹,你会发现那里有最初的 MIDI 文件。这些文件在原本的位置被替换成了指向 _portal 的链接文件,使得音乐工作站像预期一样运行。

$ ls -lG
[...] _portal/
[...] song.1.qtr
[...] song.qtr
[...] song-Track_1-1.mid -> _portal/song-Track_1-1.mid*
[...] song-Track_1-3.mid -> _portal/song-Track_1-3.mid*
[...] song-Track_2-1.mid -> _portal/song-Track_2-1.mid*

与 Git 相同,你也可以添加一个目录下的文件。

$ cp -r ~/synth-presets/yoshimi .
$ git-portal add yoshimi
Directories cannot go through the portal. Sending files instead.
$ ls -lG _portal/yoshimi
[...] yoshimi.stat -> ../_portal/yoshimi/yoshimi.stat*

删除功能也像预期一样工作,但是当从 _portal 中删除一些东西时,你应该使用 git-portal rm 而不是 git rm。使用 Git-portal 可以确保文件从 _portal 中删除:

$ ls
_portal/    song.qtr             song-Track_1-3.mid@  yoshimi/
song.1.qtr  song-Track_1-1.mid@  song-Track_2-1.mid@
$ git-portal rm song-Track_1-3.mid
rm 'song-Track_1-3.mid'
$ ls _portal/
song-Track_1-1.mid*  song-Track_2-1.mid*  yoshimi/

如果你忘记使用 Git-portal,那么你需要手动删除 _portal 下的文件:

$ git-portal rm song-Track_1-1.mid
rm 'song-Track_1-1.mid'
$ ls _portal/
song-Track_1-1.mid* song-Track_2-1.mid* yoshimi/
$ trash _portal/song-Track_1-1.mid

Git-portal 其它的唯一功能,是列出当前所有的链接并且找到里面可能已经损坏的符号链接。有时这种情况会因为项目文件夹中的文件被移动而发生:

$ mkdir foo
$ mv yoshimi foo
$ git-portal status
bigproject.git/song-Track_2-1.mid: symbolic link to _portal/song-Track_2-1.mid
bigproject.git/foo/yoshimi/yoshimi.stat: broken symbolic link to ../_portal/yoshimi/yoshimi.stat

如果你使用 Git-portal 用于私人项目并且维护自己的备份,以上就是技术方面所有你需要知道关于 Git-portal 的事情了。如果你想要添加一个协作者或者你希望 Git-portal 来像 Git 的方式来管理备份,你可以创建一个远程位置。

增加 Git-portal 远程位置

为 Git-portal 增加一个远程位置是通过 Git 已有的远程功能来实现的。Git-portal 实现了 Git 钩子(隐藏在存储库 .git 文件夹中的脚本),来寻找你的远程位置上是否存在以 _portal 开头的文件夹。如果它找到一个,它会尝试使用 rsync 来与远程位置同步文件。Git-portal 在用户进行 Git 推送以及 Git 合并的时候(或者在进行 Git 拉取的时候,实际上是进行一次获取和自动合并),都会执行此操作。

如果你仅克隆了 Git 存储库,那么你可能永远不会自己添加一个远程位置。这是一个标准的 Git 过程:

$ git remote add origin [email protected]:seth/bigproject.git
$ git remote -v
origin [email protected]:seth/bigproject.git (fetch)
origin [email protected]:seth/bigproject.git (push)

对你的主要 Git 存储库来说,origin 这个名字是一个流行的惯例,将其用于 Git 数据是有意义的。然而,你的 Git-portal 数据是分开存储的,所以你必须创建第二个远程位置来让 Git-portal 了解向哪里推送和从哪里拉取。取决于你的 Git 主机,你可能需要一个单独的服务器,因为空间有限的 Git 主机不太可能接受 GB 级的媒体资产。或者,可能你的服务器仅允许你访问你的 Git 存储库而不允许访问外部的存储文件夹:

$ git remote add _portal [email protected]:/home/seth/git/bigproject_portal
$ git remote -v
origin [email protected]:seth/bigproject.git (fetch)
origin [email protected]:seth/bigproject.git (push)
_portal [email protected]:/home/seth/git/bigproject_portal (fetch)
_portal [email protected]:/home/seth/git/bigproject_portal (push)

你可能不想为所有用户提供服务器上的个人帐户,也不必这样做。为了提供对托管资源库大文件资产的服务器的访问权限,你可以运行一个 Git 前端,比如 Gitolite 或者你可以使用 rrsync (受限的 rsync)。

现在你可以推送你的 Git 数据到你的远程 Git 存储库,并将你的 Git-portal 数据到你的远程的门户:

$ git push origin HEAD  
master destination detected
Syncing _portal content...
sending incremental file list
sent 9,305 bytes  received 18 bytes  1,695.09 bytes/sec
total size is 60,358,015  speedup is 6,474.10
Syncing _portal content to example.com:/home/seth/git/bigproject_portal

如果你已经安装了 Git-portal,并且配置了 _portal 的远程位置,你的 _portal 文件夹将会被同步,并且从服务器获取新的内容,以及在每一次推送的时候发送新的内容。尽管你不需要进行 Git 提交或者推送来和服务器同步(用户可以使用直接使用 rsync),但是我发现对于艺术性内容的改变,提交是有用的。这将会把艺术家及其数字资产集成到工作流的其余部分中,并提供有关项目进度和速度的有用元数据。

其他选择

如果 Git-portal 对你而言太过简单,还有一些用于 Git 管理大型文件的其他选择。Git 大文件存储(LFS)是一个名为 git-media 的停工项目的分支,这个分支由 GitHub 维护和支持。它需要特殊的命令(例如 git lfs track 来保护大型文件不被 Git 追踪)并且需要用户维护一个 .gitattributes 文件来更新哪些存储库中的文件被 LFS 追踪。对于大文件而言,它支持 HTTP 和 HTTPS 远程主机。所以你必须配置 LFS 服务器,才能使得用户可以通过 HTTP 而不是 SSH 或 rsync 来进行鉴权。

另一个相对 LFS 更灵活的选择是 git-annex。你可以在我的文章 管理 Git 中大二进制 blob 中了解更多(忽略其中 git-media 这个已经废弃项目的章节,因为其灵活性没有被它的继任者 Git LFS 延续下来)。Git-annex 是一个灵活且优雅的解决方案。它拥有一个细腻的系统来用于添加、删除、移动存储库中的大型文件。因为它灵活且强大,有很多新的命令和规则需要进行学习,所以建议看一下它的文档

然而,如果你的需求很简单,你可能更加喜欢整合已有技术来进行简单且明显任务的解决方案,则 Git-portal 可能是对于工作而言比较合适的工具。


via: https://opensource.com/article/19/4/manage-multimedia-files-git

作者:Seth Kenlon 选题:lujun9972 译者:svtter 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

当使用 Git 存储库时,这六个 Bash 脚本将使你的生活更轻松。

我编写了许多 Bash 脚本,这些脚本使我在使用 Git 存储库时工作更加轻松。我的许多同事说没有必要:我所做的一切都可以用 Git 命令完成。虽然这可能是正确的,但我发现脚本远比尝试找出适当的 Git 命令来执行我想要的操作更加方便。

1、gitlog

gitlog 打印针对 master 分支的当前补丁的简短列表。它从最旧到最新打印它们,并显示作者和描述,其中 H 代表 HEAD^ 代表 HEAD^2 代表 HEAD~2,依此类推。例如:

$ gitlog
-----------------------[ recovery25 ]-----------------------
(snip)
11 340d27a33895 Bob Peterson     gfs2: drain the ail2 list after io errors
10 9b3c4e6efb10 Bob Peterson     gfs2: clean up iopen glock mess in gfs2_create_inode
 9 d2e8c22be39b Bob Peterson     gfs2: Do proper error checking for go_sync family of glops
 8 9563e31f8bfd Christoph Hellwig gfs2: use page_offset in gfs2_page_mkwrite
 7 ebac7a38036c Christoph Hellwig gfs2: don't use buffer_heads in gfs2_allocate_page_backing
 6 f703a3c27874 Andreas Gruenbacher gfs2: Improve mmap write vs. punch_hole consistency
 5 a3e86d2ef30e Andreas Gruenbacher gfs2: Multi-block allocations in gfs2_page_mkwrite
 4 da3c604755b0 Andreas Gruenbacher gfs2: Fix end-of-file handling in gfs2_page_mkwrite
 3 4525c2f5b46f Bob Peterson     Rafael Aquini's slab instrumentation
 2 a06a5b7dea02 Bob Peterson     GFS2: Add go_get_holdtime to gl_ops
 ^ 8ba93c796d5c Bob Peterson     gfs2: introduce new function remaining_hold_time and use it in dq
 H e8b5ff851bb9 Bob Peterson     gfs2: Allow rgrps to have a minimum hold time

如果我想查看其他分支上有哪些补丁,可以指定一个替代分支:

$ gitlog recovery24

2、gitlog.id

gitlog.id 只是打印出补丁的 SHA1 ID:

$ gitlog.id
-----------------------[ recovery25 ]-----------------------
56908eeb6940 2ca4a6b628a1 fc64ad5d99fe 02031a00a251 f6f38da7dd18 d8546e8f0023 fc3cc1f98f6b 12c3e0cb3523 76cce178b134 6fc1dce3ab9c 1b681ab074ca 26fed8de719b 802ff51a5670 49f67a512d8c f04f20193bbb 5f6afe809d23 2030521dc70e dada79b3be94 9b19a1e08161 78a035041d3e f03da011cae2 0d2b2e068fcd 2449976aa133 57dfb5e12ccd 53abedfdcf72 6fbdda3474b3 49544a547188 187032f7a63c 6f75dae23d93 95fc2a261b00 ebfb14ded191 f653ee9e414a 0e2911cb8111 73968b76e2e3 8a3e4cb5e92c a5f2da803b5b 7c9ef68388ed 71ca19d0cba8 340d27a33895 9b3c4e6efb10 d2e8c22be39b 9563e31f8bfd ebac7a38036c f703a3c27874 a3e86d2ef30e da3c604755b0 4525c2f5b46f a06a5b7dea02 8ba93c796d5c e8b5ff851bb9

同样,它假定是当前分支,但是如果需要,我可以指定其他分支。

3、gitlog.id2

gitlog.id2gitlog.id 相同,但顶部没有显示分支的行。这对于从一个分支挑选所有补丁到当前分支很方便:

$ # 创建一个新分支
$ git branch --track origin/master
$ # 检出刚刚创建的新分支
$ git checkout recovery26
$ # 从旧的分支挑选所有补丁到新分支
$ for i in `gitlog.id2 recovery25` ; do git cherry-pick $i ;done

4、gitlog.grep

gitlog.grep 会在该补丁集合中寻找一个字符串。例如,如果我发现一个错误并想修复引用了函数 inode_go_sync 的补丁,我可以简单地执行以下操作:

$ gitlog.grep inode_go_sync
-----------------------[ recovery25 - 50 patches ]-----------------------
(snip)
11 340d27a33895 Bob Peterson     gfs2: drain the ail2 list after io errors
10 9b3c4e6efb10 Bob Peterson     gfs2: clean up iopen glock mess in gfs2_create_inode
 9 d2e8c22be39b Bob Peterson     gfs2: Do proper error checking for go_sync family of glops
152:-static void inode_go_sync(struct gfs2_glock *gl)
153:+static int inode_go_sync(struct gfs2_glock *gl)
163:@@ -296,6 +302,7 @@ static void inode_go_sync(struct gfs2_glock *gl)
 8 9563e31f8bfd Christoph Hellwig gfs2: use page_offset in gfs2_page_mkwrite
 7 ebac7a38036c Christoph Hellwig gfs2: don't use buffer_heads in gfs2_allocate_page_backing
 6 f703a3c27874 Andreas Gruenbacher gfs2: Improve mmap write vs. punch_hole consistency
 5 a3e86d2ef30e Andreas Gruenbacher gfs2: Multi-block allocations in gfs2_page_mkwrite
 4 da3c604755b0 Andreas Gruenbacher gfs2: Fix end-of-file handling in gfs2_page_mkwrite
 3 4525c2f5b46f Bob Peterson     Rafael Aquini's slab instrumentation
 2 a06a5b7dea02 Bob Peterson     GFS2: Add go_get_holdtime to gl_ops
 ^ 8ba93c796d5c Bob Peterson     gfs2: introduce new function remaining_hold_time and use it in dq
 H e8b5ff851bb9 Bob Peterson     gfs2: Allow rgrps to have a minimum hold time

因此,现在我知道补丁 HEAD~9 是需要修复的补丁。我使用 git rebase -i HEAD~10 编辑补丁 9,git commit -a --amend,然后 git rebase --continue 以进行必要的调整。

5、gitbranchcmp3

gitbranchcmp3 使我可以将当前分支与另一个分支进行比较,因此我可以将较旧版本的补丁与我的较新版本进行比较,并快速查看已更改和未更改的内容。它生成一个比较脚本(使用了 KDE 工具 Kompare,该工具也可在 GNOME3 上使用)以比较不太相同的补丁。如果除行号外没有其他差异,则打印 [SAME]。如果仅存在注释差异,则打印 [same](小写)。例如:

$ gitbranchcmp3 recovery24
Branch recovery24 has 47 patches
Branch recovery25 has 50 patches

(snip)
38 87eb6901607a 340d27a33895 [same] gfs2: drain the ail2 list after io errors
39 90fefb577a26 9b3c4e6efb10 [same] gfs2: clean up iopen glock mess in gfs2_create_inode
40 ba3ae06b8b0e d2e8c22be39b [same] gfs2: Do proper error checking for go_sync family of glops
41 2ab662294329 9563e31f8bfd [SAME] gfs2: use page_offset in gfs2_page_mkwrite
42 0adc6d817b7a ebac7a38036c [SAME] gfs2: don't use buffer_heads in gfs2_allocate_page_backing
43 55ef1f8d0be8 f703a3c27874 [SAME] gfs2: Improve mmap write vs. punch_hole consistency
44 de57c2f72570 a3e86d2ef30e [SAME] gfs2: Multi-block allocations in gfs2_page_mkwrite
45 7c5305fbd68a da3c604755b0 [SAME] gfs2: Fix end-of-file handling in gfs2_page_mkwrite
46 162524005151 4525c2f5b46f [SAME] Rafael Aquini's slab instrumentation
47              a06a5b7dea02 [    ] GFS2: Add go_get_holdtime to gl_ops
48              8ba93c796d5c [    ] gfs2: introduce new function remaining_hold_time and use it in dq
49              e8b5ff851bb9 [    ] gfs2: Allow rgrps to have a minimum hold time

Missing from recovery25:
The missing:
Compare script generated at: /tmp/compare_mismatches.sh

6、gitlog.find

最后,我有一个 gitlog.find 脚本,可以帮助我识别补丁程序的上游版本在哪里以及每个补丁的当前状态。它通过匹配补丁说明来实现。它还会生成一个比较脚本(再次使用了 Kompare),以将当前补丁与上游对应补丁进行比较:

$ gitlog.find
-----------------------[ recovery25 - 50 patches ]-----------------------
(snip)
11 340d27a33895 Bob Peterson     gfs2: drain the ail2 list after io errors
lo 5bcb9be74b2a Bob Peterson     gfs2: drain the ail2 list after io errors
10 9b3c4e6efb10 Bob Peterson     gfs2: clean up iopen glock mess in gfs2_create_inode
fn 2c47c1be51fb Bob Peterson     gfs2: clean up iopen glock mess in gfs2_create_inode
 9 d2e8c22be39b Bob Peterson     gfs2: Do proper error checking for go_sync family of glops
lo feb7ea639472 Bob Peterson     gfs2: Do proper error checking for go_sync family of glops
 8 9563e31f8bfd Christoph Hellwig gfs2: use page_offset in gfs2_page_mkwrite
ms f3915f83e84c Christoph Hellwig gfs2: use page_offset in gfs2_page_mkwrite
 7 ebac7a38036c Christoph Hellwig gfs2: don't use buffer_heads in gfs2_allocate_page_backing
ms 35af80aef99b Christoph Hellwig gfs2: don't use buffer_heads in gfs2_allocate_page_backing
 6 f703a3c27874 Andreas Gruenbacher gfs2: Improve mmap write vs. punch_hole consistency
fn 39c3a948ecf6 Andreas Gruenbacher gfs2: Improve mmap write vs. punch_hole consistency
 5 a3e86d2ef30e Andreas Gruenbacher gfs2: Multi-block allocations in gfs2_page_mkwrite
fn f53056c43063 Andreas Gruenbacher gfs2: Multi-block allocations in gfs2_page_mkwrite
 4 da3c604755b0 Andreas Gruenbacher gfs2: Fix end-of-file handling in gfs2_page_mkwrite
fn 184b4e60853d Andreas Gruenbacher gfs2: Fix end-of-file handling in gfs2_page_mkwrite
 3 4525c2f5b46f Bob Peterson     Rafael Aquini's slab instrumentation
   Not found upstream
 2 a06a5b7dea02 Bob Peterson     GFS2: Add go_get_holdtime to gl_ops
   Not found upstream
 ^ 8ba93c796d5c Bob Peterson     gfs2: introduce new function remaining_hold_time and use it in dq
   Not found upstream
 H e8b5ff851bb9 Bob Peterson     gfs2: Allow rgrps to have a minimum hold time
   Not found upstream
Compare script generated: /tmp/compare_upstream.sh

补丁显示为两行,第一行是你当前的修补程序,然后是相应的上游补丁,以及 2 个字符的缩写,以指示其上游状态:

  • lo 表示补丁仅在本地(local)上游 Git 存储库中(即尚未推送到上游)。
  • ms 表示补丁位于 Linus Torvald 的主(master)分支中。
  • fn 意味着补丁被推送到我的 “for-next” 开发分支,用于下一个上游合并窗口。 我的一些脚本根据我通常使用 Git 的方式做出假设。例如,当搜索上游补丁时,它使用我众所周知的 Git 树的位置。因此,你需要调整或改进它们以适合你的条件。gitlog.find 脚本旨在仅定位 GFS2DLM 补丁,因此,除非你是 GFS2 开发人员,否则你需要针对你感兴趣的组件对其进行自定义。

源代码

以下是这些脚本的源代码。

1、gitlog

#!/bin/bash
branch=$1

if test "x$branch" = x; then
    branch=`git branch -a | grep "*" | cut -d ' ' -f2`
fi

patches=0
tracking=`git rev-parse --abbrev-ref --symbolic-full-name @{u}`

LIST=`git log --reverse --abbrev-commit --pretty=oneline $tracking..$branch | cut -d ' ' -f1 |paste -s -d ' '`
for i in $LIST; do patches=$(echo $patches + 1 | bc);done

if [[ $branch =~ .*for-next.* ]]
then
    start=HEAD
#    start=origin/for-next
else
    start=origin/master
fi

tracking=`git rev-parse --abbrev-ref --symbolic-full-name @{u}`

/usr/bin/echo "-----------------------[" $branch "]-----------------------"
patches=$(echo $patches - 1 | bc);
for i in $LIST; do
    if [ $patches -eq 1 ]; then
        cnt=" ^"
    elif [ $patches -eq 0 ]; then
        cnt=" H"
    else
        if [ $patches -lt 10 ]; then
            cnt=" $patches"
        else
            cnt="$patches"
        fi
    fi
    /usr/bin/git show --abbrev-commit -s --pretty=format:"$cnt %h %<|(32)%an %s %n" $i
    patches=$(echo $patches - 1 | bc)
done
#git log --reverse --abbrev-commit --pretty=format:"%h %<|(32)%an %s" $tracking..$branch
#git log --reverse --abbrev-commit --pretty=format:"%h %<|(32)%an %s" ^origin/master ^linux-gfs2/for-next $branch

2、gitlog.id

#!/bin/bash
branch=$1

if test "x$branch" = x; then
    branch=`git branch -a | grep "*" | cut -d ' ' -f2`
fi

tracking=`git rev-parse --abbrev-ref --symbolic-full-name @{u}`

/usr/bin/echo "-----------------------[" $branch "]-----------------------"
git log --reverse --abbrev-commit --pretty=oneline $tracking..$branch | cut -d ' ' -f1 |paste -s -d ' '

3、gitlog.id2

#!/bin/bash
branch=$1

if test "x$branch" = x; then
    branch=`git branch -a | grep "*" | cut -d ' ' -f2`
fi

tracking=`git rev-parse --abbrev-ref --symbolic-full-name @{u}`
git log --reverse --abbrev-commit --pretty=oneline $tracking..$branch | cut -d ' ' -f1 |paste -s -d ' '

4、gitlog.grep

#!/bin/bash
param1=$1
param2=$2

if test "x$param2" = x; then
    branch=`git branch -a | grep "*" | cut -d ' ' -f2`
    string=$param1
else
    branch=$param1
    string=$param2
fi

patches=0
tracking=`git rev-parse --abbrev-ref --symbolic-full-name @{u}`

LIST=`git log --reverse --abbrev-commit --pretty=oneline $tracking..$branch | cut -d ' ' -f1 |paste -s -d ' '`
for i in $LIST; do patches=$(echo $patches + 1 | bc);done
/usr/bin/echo "-----------------------[" $branch "-" $patches "patches ]-----------------------"
patches=$(echo $patches - 1 | bc);
for i in $LIST; do
    if [ $patches -eq 1 ]; then
        cnt=" ^"
    elif [ $patches -eq 0 ]; then
        cnt=" H"
    else
        if [ $patches -lt 10 ]; then
            cnt=" $patches"
        else
            cnt="$patches"
        fi
    fi
    /usr/bin/git show --abbrev-commit -s --pretty=format:"$cnt %h %<|(32)%an %s" $i
    /usr/bin/git show --pretty=email --patch-with-stat $i | grep -n "$string"
    patches=$(echo $patches - 1 | bc)
done

5、gitbranchcmp3

#!/bin/bash
#
# gitbranchcmp3 <old branch> [<new_branch>]
#
oldbranch=$1
newbranch=$2
script=/tmp/compare_mismatches.sh

/usr/bin/rm -f $script
echo "#!/bin/bash" > $script
/usr/bin/chmod 755 $script
echo "# Generated by gitbranchcmp3.sh" >> $script
echo "# Run this script to compare the mismatched patches" >> $script
echo " " >> $script
echo "function compare_them()" >> $script
echo "{"  >> $script
echo "    git show --pretty=email --patch-with-stat \$1 > /tmp/gronk1" >> $script
echo "    git show --pretty=email --patch-with-stat \$2 > /tmp/gronk2" >> $script
echo "    kompare /tmp/gronk1 /tmp/gronk2" >> $script
echo "}" >> $script
echo " " >> $script

if test "x$newbranch" = x; then
    newbranch=`git branch -a | grep "*" | cut -d ' ' -f2`
fi

tracking=`git rev-parse --abbrev-ref --symbolic-full-name @{u}`

declare -a oldsha1s=(`git log --reverse --abbrev-commit --pretty=oneline $tracking..$oldbranch | cut -d ' ' -f1 |paste -s -d ' '`)
declare -a newsha1s=(`git log --reverse --abbrev-commit --pretty=oneline $tracking..$newbranch | cut -d ' ' -f1 |paste -s -d ' '`)

#echo "old: " $oldsha1s
oldcount=${#oldsha1s[@]}
echo "Branch $oldbranch has $oldcount patches"
oldcount=$(echo $oldcount - 1 | bc)
#for o in `seq 0 ${#oldsha1s[@]}`; do
#    echo -n ${oldsha1s[$o]} " "
#    desc=`git show $i | head -5 | tail -1|cut -b5-`
#done

#echo "new: " $newsha1s
newcount=${#newsha1s[@]}
echo "Branch $newbranch has $newcount patches"
newcount=$(echo $newcount - 1 | bc)
#for o in `seq 0 ${#newsha1s[@]}`; do
#    echo -n ${newsha1s[$o]} " "
#    desc=`git show $i | head -5 | tail -1|cut -b5-`
#done
echo

for new in `seq 0 $newcount`; do
    newsha=${newsha1s[$new]}
    newdesc=`git show $newsha | head -5 | tail -1|cut -b5-`
    oldsha="            "
    same="[    ]"
    for old in `seq 0 $oldcount`; do
        if test "${oldsha1s[$old]}" = "match"; then
            continue;
        fi
        olddesc=`git show ${oldsha1s[$old]} | head -5 | tail -1|cut -b5-`
        if test "$olddesc" = "$newdesc" ; then
            oldsha=${oldsha1s[$old]}
            #echo $oldsha
            git show $oldsha |tail -n +2 |grep -v "index.*\.\." |grep -v "@@" > /tmp/gronk1
            git show $newsha |tail -n +2 |grep -v "index.*\.\." |grep -v "@@"  > /tmp/gronk2
            diff /tmp/gronk1 /tmp/gronk2 &> /dev/null
            if [ $? -eq 0 ] ;then
# No differences
                same="[SAME]"
                oldsha1s[$old]="match"
                break
            fi
            git show $oldsha |sed -n '/diff/,$p' |grep -v "index.*\.\." |grep -v "@@" > /tmp/gronk1
            git show $newsha |sed -n '/diff/,$p' |grep -v "index.*\.\." |grep -v "@@" > /tmp/gronk2
            diff /tmp/gronk1 /tmp/gronk2 &> /dev/null
            if [ $? -eq 0 ] ;then
# Differences in comments only
                same="[same]"
                oldsha1s[$old]="match"
                break
            fi
            oldsha1s[$old]="match"
            echo "compare_them $oldsha $newsha" >> $script
        fi
    done
    echo "$new $oldsha $newsha $same $newdesc"
done

echo
echo "Missing from $newbranch:"
the_missing=""
# Now run through the olds we haven't matched up
for old in `seq 0 $oldcount`; do
    if test ${oldsha1s[$old]} != "match"; then
        olddesc=`git show ${oldsha1s[$old]} | head -5 | tail -1|cut -b5-`
        echo "${oldsha1s[$old]} $olddesc"
        the_missing=`echo "$the_missing ${oldsha1s[$old]}"`
    fi
done

echo "The missing: " $the_missing
echo "Compare script generated at: $script"
#git log --reverse --abbrev-commit --pretty=oneline $tracking..$branch | cut -d ' ' -f1 |paste -s -d ' '

6、gitlog.find

#!/bin/bash
#
# Find the upstream equivalent patch
#
# gitlog.find
#
cwd=$PWD
param1=$1
ubranch=$2
patches=0
script=/tmp/compare_upstream.sh
echo "#!/bin/bash" > $script
/usr/bin/chmod 755 $script
echo "# Generated by gitbranchcmp3.sh" >> $script
echo "# Run this script to compare the mismatched patches" >> $script
echo " " >> $script
echo "function compare_them()" >> $script
echo "{"  >> $script
echo "    cwd=$PWD" >> $script
echo "    git show --pretty=email --patch-with-stat \$2 > /tmp/gronk2" >> $script
echo "    cd ~/linux.git/fs/gfs2" >> $script
echo "    git show --pretty=email --patch-with-stat \$1 > /tmp/gronk1" >> $script
echo "    cd $cwd" >> $script
echo "    kompare /tmp/gronk1 /tmp/gronk2" >> $script
echo "}" >> $script
echo " " >> $script

#echo "Gathering upstream patch info. Please wait."
branch=`git branch -a | grep "*" | cut -d ' ' -f2`
tracking=`git rev-parse --abbrev-ref --symbolic-full-name @{u}`

cd ~/linux.git
if test "X${ubranch}" = "X"; then
    ubranch=`git branch -a | grep "*" | cut -d ' ' -f2`
fi
utracking=`git rev-parse --abbrev-ref --symbolic-full-name @{u}`
#
# gather a list of gfs2 patches from master just in case we can't find it
#
#git log --abbrev-commit --pretty=format:"   %h %<|(32)%an %s" master |grep -i -e "gfs2" -e "dlm" > /tmp/gronk
git log --reverse --abbrev-commit --pretty=format:"ms %h %<|(32)%an %s" master fs/gfs2/ > /tmp/gronk.gfs2
# ms = in Linus's master
git log --reverse --abbrev-commit --pretty=format:"ms %h %<|(32)%an %s" master fs/dlm/ > /tmp/gronk.dlm

cd $cwd
LIST=`git log --reverse --abbrev-commit --pretty=oneline $tracking..$branch | cut -d ' ' -f1 |paste -s -d ' '`
for i in $LIST; do patches=$(echo $patches + 1 | bc);done
/usr/bin/echo "-----------------------[" $branch "-" $patches "patches ]-----------------------"
patches=$(echo $patches - 1 | bc);
for i in $LIST; do
    if [ $patches -eq 1 ]; then
        cnt=" ^"
    elif [ $patches -eq 0 ]; then
        cnt=" H"
    else
        if [ $patches -lt 10 ]; then
            cnt=" $patches"
        else
            cnt="$patches"
        fi
    fi
    /usr/bin/git show --abbrev-commit -s --pretty=format:"$cnt %h %<|(32)%an %s" $i
    desc=`/usr/bin/git show --abbrev-commit -s --pretty=format:"%s" $i`
    cd ~/linux.git
    cmp=1
    up_eq=`git log --reverse --abbrev-commit --pretty=format:"lo %h %<|(32)%an %s" $utracking..$ubranch | grep "$desc"`
# lo = in local for-next
    if test "X$up_eq" = "X"; then
        up_eq=`git log --reverse --abbrev-commit --pretty=format:"fn %h %<|(32)%an %s" master..$utracking | grep "$desc"`
# fn = in for-next for next merge window
        if test "X$up_eq" = "X"; then
            up_eq=`grep "$desc" /tmp/gronk.gfs2`
            if test "X$up_eq" = "X"; then
                up_eq=`grep "$desc" /tmp/gronk.dlm`
                if test "X$up_eq" = "X"; then
                    up_eq="   Not found upstream"
                    cmp=0
                fi
            fi
        fi
    fi
    echo "$up_eq"
    if [ $cmp -eq 1 ] ; then
        UP_SHA1=`echo $up_eq|cut -d' ' -f2`
        echo "compare_them $UP_SHA1 $i" >> $script
    fi
    cd $cwd
    patches=$(echo $patches - 1 | bc)
done
echo "Compare script generated: $script"

via: https://opensource.com/article/20/1/bash-scripts-git

作者:Bob Peterson 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

借助 Gitolite,你可以使用 Git 来管理 Git 服务器。在我们的系列文章中了解这些鲜为人知的 Git 用途。

正如我在系列文章中演示的那样,Git 除了跟踪源代码外,还可以做很多事情。信不信由你,Git 甚至可以管理你的 Git 服务器,因此你可以或多或少地使用 Git 本身来运行 Git 服务器。

当然,这涉及除日常使用 Git 之外的许多组件,其中最重要的是 Gitolite,该后端应用程序可以管理你使用 Git 的每个细微的配置。Gitolite 的优点在于,由于它使用 Git 作为其前端接口,因此很容易将 Git 服务器管理集成到其他基于 Git 的工作流中。Gitolite 可以精确控制谁可以访问你服务器上的特定存储库以及他们具有哪些权限。你可以使用常规的 Linux 系统工具自行管理此类事务,但是如果有好几个用户和不止一两个仓库,则需要大量的工作。

Gitolite 的开发人员做了艰苦的工作,使你可以轻松地为许多用户提供对你的 Git 服务器的访问权,而又不让他们访问你的整个环境 —— 而这一切,你可以使用 Git 来完成全部工作。

Gitolite 并不是图形化的管理员和用户面板。优秀的 Gitea 项目可提供这种体验,但是本文重点介绍 Gitolite 的简单优雅和令人舒适的熟悉感。

安装 Gitolite

假设你的 Git 服务器运行在 Linux 上,则可以使用包管理器安装 Gitolite(在 CentOS 和 RHEL 上为 yum,在 Debian 和 Ubuntu 上为 apt,在 OpenSUSE 上为 zypper 等)。例如,在 RHEL 上:

$ sudo yum install gitolite3

许多发行版的存储库提供的仍是旧版本的 Gitolite,但最新版本为版本 3。

你必须具有对服务器的无密码 SSH 访问权限。如果愿意,你可以使用密码登录服务器,但是 Gitolite 依赖于 SSH 密钥,因此必须配置使用密钥登录的选项。如果你不知道如何配置服务器以进行无密码 SSH 访问,请首先学习如何进行操作(Steve Ovens 的 Ansible 文章的设置 SSH 密钥身份验证部分对此进行了很好的说明)。这是加强服务器管理的安全以及运行 Gitolite 的重要组成部分。

配置 Git 用户

如果没有 Gitolite,则如果某人请求访问你在服务器上托管的 Git 存储库时,则必须向该人提供用户帐户。Git 提供了一个特殊的外壳,即 git-shell,这是一个仅执行 Git 任务的特别的特定 shell。这可以让你有个只能通过非常受限的 Shell 环境来过滤访问你的服务器的用户。

这个解决方案是一个办法,但通常意味着用户可以访问服务器上的所有存储库,除非你具有用于组权限的良好模式,并在创建新存储库时严格遵循这些权限。这种方式还需要在系统级别进行大量手动配置,这通常是只有特定级别的系统管理员才能做的工作,而不一定是通常负责 Git 存储库的人员。

Gitolite 通过为需要访问任何存储库的每个人指定一个用户名来完全回避此问题。默认情况下,该用户名是 git,并且由于 Gitolite 的文档中假定使用的是它,因此在学习该工具时保留它是一个很好的默认设置。对于曾经使用过 GitLab 或 GitHub 或任何其他 Git 托管服务的人来说,这也是一个众所周知的约定。

Gitolite 将此用户称为托管用户。在服务器上创建一个帐户以充当托管用户(我习惯使用 git,因为这是惯例):

$ sudo adduser --create-home git

为了控制该 git 用户帐户,该帐户必须具有属于你的有效 SSH 公钥。你应该已经进行了设置,因此复制你的公钥(而不是你的私钥)添加到 git 用户的家目录中:

$ sudo cp ~/.ssh/id_ed25519.pub /home/git/
$ sudo chown git:git /home/git/id_ed25519.pub

如果你的公钥不以扩展名 .pub 结尾,则 Gitolite 不会使用它,因此请相应地重命名该文件。切换为该用户帐户以运行 Gitolite 的安装程序:

$ sudo su - git
$ gitolite setup --pubkey id_ed25519.pub

安装脚本运行后,git 的家用户目录将有一个 repository 目录,该目录(目前)包含存储库 git-admin.gittesting.git。这就是该服务器所需的全部设置,现在请登出 git 用户。

使用 Gitolite

管理 Gitolite 就是编辑 Git 存储库中的文本文件,尤其是 gitolite-admin.git 中的。你不会通过 SSH 进入服务器来进行 Git 管理,并且 Gitolite 也建议你不要这样尝试。在 Gitolite 服务器上存储你和你的用户的存储库是个存储库,因此最好不要使用它们。

$ git clone [email protected]:gitolite-admin.git gitolite-admin.git
$ cd gitolite-admin.git
$ ls -1
conf
keydir

该存储库中的 conf 目录包含一个名为 gitolite.conf 的文件。在文本编辑器中打开它,或使用 cat 查看其内容:

repo gitolite-admin
    RW+     =   id_ed22519

repo testing
    RW+     =   @all

你可能对该配置文件的功能有所了解:gitolite-admin 代表此存储库,并且 id_ed25519 密钥的所有者具有读取、写入和管理 Git 的权限。换句话说,不是将用户映射到普通的本地 Unix 用户(因为所有用户都使用 git 用户托管用户身份),而是将用户映射到 keydir 目录中列出的 SSH 密钥。

testing.git 存储库使用特殊组符号为访问服务器的每个人提供了全部权限。

添加用户

如果要向 Git 服务器添加一个名为 alice 的用户,Alice 必须向你发送她的 SSH 公钥。Gitolite 使用文件名的 .pub 扩展名左边的任何内容作为该 Git 用户的标识符。不要使用默认的密钥名称值,而是给密钥指定一个指示密钥所有者的名称。如果用户有多个密钥(例如,一个用于笔记本电脑,一个用于台式机),则可以使用子目录来避免文件名冲突。例如,Alice 在笔记本电脑上使用的密钥可能是默认的 id_rsa.pub,因此将其重命名为alice.pub 或类似名称(或让用户根据其计算机上的本地用户帐户来命名密钥),然后将其放入 gitolite-admin.git/keydir/work/laptop/ 目录中。如果她从她的桌面计算机发送了另一个密钥,命名为 alice.pub(与上一个相同),然后将其添加到 keydir/home/desktop/ 中。另一个密钥可能放到 keydir/home/desktop/ 中,依此类推。Gitolite 递归地在 keydir 中搜索与存储库“用户”相匹配的 .pub 文件,并将所有匹配项视为相同的身份。

当你将密钥添加到 keydir 目录时,必须将它们提交回服务器。这是一件很容易忘记的事情,这里有一个使用自动化的 Git 应用程序(例如 Sparkleshare)的真正的理由,因此任何更改都将立即提交给你的 Gitolite 管理员。第一次忘记提交和推送,在浪费了三个小时的你和你的用户的故障排除时间之后,你会发现 Gitolite 是使用 Sparkleshare 的完美理由。

$ git add keydir
$ git commit -m 'added alice-laptop-0.pub'
$ git push origin HEAD

默认情况下,Alice 可以访问 testing.git 目录,因此她可以使用该目录测试连接性和功能。

设置权限

与用户一样,目录权限和组也是从你可能习惯的的常规 Unix 工具中抽象出来的(或可从在线信息查找)。在 gitolite-admin.git/conf 目录中的 gitolite.conf 文件中授予对项目的权限。权限分为四个级别:

  • R 允许只读。在存储库上具有 R 权限的用户可以克隆它,仅此而已。
  • RW 允许用户执行分支的快进推送、创建新分支和创建新标签。对于大多数用户来说,这个基本上就像是一个“普通”的 Git 存储库。
  • RW+ 允许可能具有破坏性的 Git 动作。用户可以执行常规的快进推送、回滚推送、变基以及删除分支和标签。你可能想要或不希望将其授予项目中的所有贡献者。
  • - 明确拒绝访问存储库。这与未在存储库的配置中列出的用户相同。

通过调整 gitolite.conf 来创建一个新的存储库或修改现有存储库的权限。例如,授予 Alice 权限来管理一个名为 widgets.git 的新存储库:

repo gitolite-admin
    RW+     =   id_ed22519

repo testing
    RW+     =   @all

repo widgets
    RW+     =   alice

现在,Alice(也仅有 Alice 一个人)可以克隆该存储库:

[alice]$ git clone [email protected]:widgets.git
Cloning into 'widgets'...
warning: You appear to have cloned an empty repository.

在第一次推送时,Alice 必须使用 -u 选项将其分支发送到空存储库(如同她在任何 Git 主机上做的一样)。

为了简化用户管理,你可以定义存储库组:

@qtrepo = widgets
@qtrepo = games

repo gitolite-admin
    RW+     =   id_ed22519

repo testing
    RW+     =   @all

repo @qtrepo
    RW+     =   alice

正如你可以创建组存储库一样,你也可以对用户进行分组。默认情况下存在一个用户组:@all。如你所料,它包括所有用户,无一例外。你也可以创建自己的组:

@qtrepo = widgets
@qtrepo = games

@developers = alice bob

repo gitolite-admin
    RW+     =   id_ed22519

repo testing
    RW+     =   @all

repo @qtrepo
    RW+     =   @developers

与添加或修改密钥文件一样,对 gitolite.conf 文件的任何更改都必须提交并推送以生效。

创建存储库

默认情况下,Gitolite 假设存储库的创建是从上至下进行。例如,有权访问 Git 服务器的项目经理创建了一个项目存储库,并通过 Gitolite 管理仓库添加了开发人员。

实际上,你可能更愿意向用户授予创建存储库的权限。Gitolite 称这些为“ 野生仓库(通配仓库) wild repos ”(我不确定这是关于仓库的形成方式的描述,还是指配置文件所需的通配符)。这是一个例子:

@managers = alice bob

repo foo/CREATOR/[a-z]..*
    C   =   @managers
    RW+ =   CREATOR
    RW  =   WRITERS
    R   =   READERS

第一行定义了一组用户:该组称为 @managers,其中包含用户 alicebob。下一行设置了通配符允许创建尚不存在的存储库,放在名为 foo 的目录下的创建该存储库的用户名的子目录中。例如:

[alice]$ git clone [email protected]:foo/alice/cool-app.git
Cloning into cool-app'...
Initialized empty Git repository in /home/git/repositories/foo/alice/cool-app.git
warning: You appear to have cloned an empty repository.

野生仓库的创建者可以使用一些机制来定义谁可以读取和写入其存储库,但是他们是有范围限定的。在大多数情况下,Gitolite 假定由一组特定的用户来管理项目权限。一种解决方案是使用 Git 挂钩来授予所有用户对 gitolite-admin 的访问权限,以要求管理者批准将更改合并到 master 分支中。

了解更多

Gitolite 具有比此介绍性文章所涵盖的更多功能,因此请尝试一下。其文档非常出色,一旦你通读了它,就可以自定义 Gitolite 服务器,以向用户提供你喜欢的任何级别的控制。Gitolite 是一种维护成本低、简单的系统,你可以安装、设置它,然后基本上就可以将其忘却。


via: https://opensource.com/article/19/4/server-administration-git

作者:Seth Kenlon 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

SparkleShare 是一个开源的基于 Git 的 Dropbox 风格的文件共享应用程序。在我们的系列文章中了解有关 Git 鲜为人知的用法。

Git 是一个少有的能将如此多的现代计算封装到一个程序之中的应用程序,它可以用作许多其他应用程序的计算引擎。虽然它以跟踪软件开发中的源代码更改而闻名,但它还有许多其他用途,可以让你的生活更轻松、更有条理。在这个 Git 系列中,我们将分享七种鲜为人知的使用 Git 的方法。

今天,我们将看看 SparkleShare,它使用 Git 作为文件共享的基础。

用于文件共享的 Git

Git 的优点之一是它具有固有的分发能力。它可用来建立共享。即使你只是与自己网络上的其他计算机共享资源库,Git 也会为从共享位置获取文件的行为带来透明性。

随着其界面的发展,Git 变得非常简单。虽然因用户而异,他们坐下来完成一些工作时的共同点仅仅是 git pull 或稍微复杂一点的 git pull && git checkout -b my-branch。但是,对于某些人来说,将命令输入到他们的计算机中的做法完全是令人困惑或烦恼的。计算机旨在使生活变得轻松,它擅长于重复性工作,因此有更简便的方法可以与 Git 共享文件。

SparkleShare

SparkleShare 项目是一个基于 Git 的跨平台的、开源的 Dropbox 式的文件共享应用程序。它通过将文件拖放到专门指定的 SparkleShare 目录中的简单操作,自动执行所有 Git 命令,触发添加、提交、推送和拉取过程。因为它基于 Git,所以你可以获得基于差异(diff)的快速推送和拉取,并且继承了 Git 版本控制和后端基础设施(如 Git 挂钩)的所有优点。它可以完全自托管,也可以将其与 GitLab、GitHub、Bitbucket 等 Git 托管服务一起使用。此外,由于它基本上只是一个 Git 的前端,因此你可以在可能没有 SparkleShare 客户端但有 Git 客户端的设备上访问 SparkleShare 中的文件。

正如你获得 Git 的所有好处一样,你也会受到所有常见的 Git 限制:使用 SparkleShare 存储数百张照片、音乐和视频是不切实际的,因为 Git 是为文本而设计和优化的。Git 当然可以存储二进制文件的大文件,但是因为它可以跟踪历史记录,因此一旦将文件添加到其中,几乎就不可能完全删除它。这在某种程度上限制了 SparkleShare 对某些人的实用性,但使其非常适合许多工作流程,包括日程安排

安装 SparkleShare

SparkleShare 是跨平台的,可从网站获得适用于 Windows 和 Mac 的安装程序。对于 Linux,有一个 Flatpak 安装包,或者你可以在终端中运行以下命令:

$ sudo flatpak remote-add flathub https://flathub.org/repo/flathub.flatpakrepo
$ sudo flatpak install flathub org.sparkleshare.SparkleShare

创建一个 Git 存储库

SparkleShare 并不是软件即服务(SaaS)。你在计算机上运行 SparkleShare 与 Git 存储库进行通信,而 SparkleShare 并不存储你的数据。如果你还没有与文件夹同步的 Git 存储库,则必须在启动 SparkleShare 之前创建一个文件夹。你有三个选择:托管的 Git、自托管 Git 或自托管 SparkleShare。

托管的 Git

SparkleShare 可以使用你能访问的任何 Git 存储库进行存储,因此,如果你拥有 GitLab 或任何其他托管服务的帐户(或创建一个),则它可以成为 SparkleShare 的后端。例如,开源 Notabug.org 服务是一个类似于 GitHub 和 GitLab 的 Git 托管服务,但其独特性足以证明 SparkleShare 的灵活性。根据用户界面的不同,不同的托管服务创建新存储库的方法也有所不同,但是所有主要存储库都遵循相同的通用模型。

首先,在托管服务中找到创建新项目或存储库的按钮,单击它以开始。然后逐步完成存储库的创建过程,为存储库提供名称、隐私级别(存储库通常默认为公共),以及是否使用 README 文件初始化存储库。无论你是否需要个 README 文件,请初始化建立一个。使用一个文件来创建存储库不是绝对必要的,但是它会强制 Git 主机实例化存储库中的 master 分支,这有助于确保前端应用程序(例如 SparkleShare)具有要提交并推送的分支。即使文件是几乎空的 README 文件,也可以用来查看该文件以确认你已连接成功。

 title=

创建存储库后,获取其用于 SSH 克隆的 URL。就像从 Git 项目获得其 URL 一样,你也可以获取此 URL:导航至存储库页面并查找 “Clone” 按钮或字段。

 title=

GitHub 的克隆 URL。

 title=

GitLab 的克隆 URL。

这是 SparkleShare 用于获取数据的地址,因此请记下它。你的 Git 存储库现已配置好。

自托管的 Git

你可以使用 SparkleShare 访问你有权访问的任何计算机上的 Git 存储库。除了一个 Git 裸存储库外,无需任何特殊设置。但是,如果你想将对 Git 存储库的访问权授予其他任何人,则应运行 Gitolite 之类的 Git 管理器或 SparkleShare 自己的 Dazzle 服务器来帮助你管理 SSH 密钥和帐户。至少,创建一个特定于 Git 的用户,以便有权访问你的 Git 存储库的用户不会自动获得对服务器其余部分的访问权限。

以 Git 用户身份登录服务器(如果你非常擅长管理用户和组权限,则可以以自己的用户登录)并创建存储库:

$ mkdir ~/sparkly.git
$ cd ~/sparkly.git
$ git init --bare .

你的 Git 存储库现已配置好。

Dazzle

SparkleShare 的开发人员提供了一个名为 Dazzle 的 Git 管理系统,以帮助你自托管 Git 存储库。

在你的服务器上,将 Dazzle 应用程序下载到你的路径中的某个位置:

$ curl https://raw.githubusercontent.com/hbons/Dazzle/master/dazzle.sh --output ~/bin/dazzle
$ chmod +x ~/bin/dazzle

Dazzle 设置了一个特定于 Git 和 SparkleShare 的用户,并且还基于 SparkleShare 应用程序生成的密钥实现了访问权限。现在,只需设置一个项目:

$ dazzle create sparkly

你的服务器现在已经配置好,可以用作 SparkleShare 托管了。

配置 SparkleShare

首次启动 SparkleShare 时,系统会提示你配置 SparkleShare 用于存储的服务器。这个过程可能看起来像一个首次运行的安装向导,但实际上是在 SparkleShare 中设置新共享位置的通常过程。与许多共享驱动器应用程序不同,使用 SparkleShare 可以一次配置多个位置。你配置的第一个共享位置并不比你以后可以配置的任何共享位置更重要,并且你也不用注册 SparkleShare 或任何其他服务。你只是将 SparkleShare 指向 Git 存储库,以便它知道如何使第一个 SparkleShare 文件夹保持同步。

在第一个屏幕上,给出一个身份信息,SparkleShare 将在代表你进行的 Git 提交记录中使用这些信息。你可以使用任何内容,甚至可以使用不代表任何意义的伪造信息。它仅用于提交消息,如果你对审查 Git 后端进程没有兴趣,你可能甚至看不到它们。

下一个屏幕提示你选择主机类型。如果你使用的是 GitLab、GitHub、Planio 或 Bitbucket,则可以选择一个适当的。否则,请选择“自己的服务器”。

 title=

在此屏幕底部,你必须输入 SSH 的克隆 URL。如果你是自托管的 Git,则地址类似于 <ssh://[email protected]>,而远程路径是为此目的而创建的 Git 存储库的绝对路径。

根据上面的自托管示例,我虚构的服务器的地址为 ssh://[email protected]:22122:22122 表示一个非标准的 SSH 端口),远程路径为 /home/git/sparkly.git

如果我改用 Notabug.org 帐户,则上例中的地址为 ssh://[email protected],路径为 seth/sparkly.git

SparkleShare 首次尝试连接到主机时会失败,因为你尚未将 SparkleShare 客户端 ID(特定于 SparkleShare 应用程序的 SSH 密钥)复制到 Git 主机。这是预料之中的,所以不要取消该过程。将 SparkleShare 设置窗口保持打开状态,并从系统任务栏中的 SparkleShare 图标处获取客户端 ID。然后将客户端 ID 复制到剪贴板,以便可以将其添加到 Git 主机。

 title=

将你的客户端 ID 添加到托管的 Git 帐户

除了较小的 UI 差异外,在任何托管服务上添加 SSH 密钥(所有客户端 ID 都是这样)的过程基本上是相同的。在你的 Git 主机的 Web 仪表板中,导航到你的用户设置,然后找到 “SSH 密钥”类别。单击“添加新密钥”按钮(或类似按钮),然后粘贴你的 SparkleShare 客户端 ID 的内容。

 title=

保存密钥。如果你希望其他人(例如协作者或家庭成员)能够访问同一存储库,则他们必须向你提供其 SparkleShare 客户端 ID,以便你可以将其添加到帐户中。

将你的客户端 ID 添加到自托管的 Git 帐户

SparkleShare 客户端 ID 只是一个 SSH 密钥,因此将其复制并粘贴到 Git 用户的 ~/.ssh/authorized_keys 文件中。

使用 Dazzle 添加你的客户 ID

如果你使用 Dazzle 管理 SparkleShare 项目,请使用以下命令添加客户端 ID:

$ dazzle link

当 Dazzle 提示你输入该 ID 时,请粘贴在 SparkleShare 菜单中找到的客户端 ID。

使用 SparkleShare

将客户端 ID 添加到 Git 主机后,在 SparkleShare 窗口中单击“重试”按钮以完成设置。克隆存储库完成后,你可以关闭 SparkleShare 设置窗口,并在你的家目录中找到一个新的 SparkleShare 文件夹。如果你设置了带有托管服务的 Git 存储库,并选择包括 README 文件或许可证文件,则可以在 SparkleShare 目录中看到它们。

 title=

此外,有一些隐藏目录,你可以通过在文件管理器中显示隐藏目录来查看。

使用 SparkleShare 的方式与使用计算机上任何目录的方式相同:将文件放入其中。每当将文件或目录放入 SparkleShare 文件夹时,它都会在后台复制到你的 Git 存储库。

排除某些文件

由于 Git 从设计上就是要记住一切,因此你可能希望从记录中排除特定的文件类型。排除一些文件是有原因的。通过定义摆脱 SparkleShare 管理的文件,可以避免意外复制大文件。你还可以为自己设计一种方案,使你可以将存储在一个目录中的逻辑上属于同一个文件(例如,MIDI 文件及其 .flac 导出文件),但是可以自己手动备份大文件,而同时让 SparkleShare 备份基于文本的文件。

如果在系统的文件管理器中看不到隐藏的文件,请显示它们。导航到你的 SparkleShare 文件夹,然后到代表你的存储库的目录,找到一个名为 .gitignore 的文件,然后在文本编辑器中将其打开。你可以在 .gitignore 中输入文件扩展名或文件名(每行一个),任何与你列出的文件匹配的文件都会被忽略(如文件名所示)。

Thumbs.db
$RECYCLE.BIN/
.DS_Store
._*
.fseventsd
.Spotlight-V100
.Trashes
.directory
.Trash-*
*.wav
*.ogg
*.flac
*.mp3
*.m4a
*.opus
*.jpg
*.png
*.mp4
*.mov
*.mkv
*.avi
*.pdf
*.djvu
*.epub
*.od{s,t}
*.cbz

你知道最经常遇到哪些文件类型,因此请集中精力处理最有可能潜入你的 SparkleShare 目录的文件。如果你想稍微矫枉过正一些,可以在 Notabug.org 以及整个网上找到 .gitignore 文件的好集合。

通过将这些条目保存在 .gitignore 文件中,你可以将不需要发送到 Git 主机的大文件放在 SparkleShare 目录中,SparkleShare 将完全忽略它们。当然,这意味着你需要确保它们可以备份或通过其他方式分发给你的 SparkleShare 协作者。

自动化

自动化 是我们与计算机达成的默契之一:计算机执行重复的、无聊的工作,而我们人类要么不擅长做这些,要么不擅长记忆这些。SparkleShare 是一种很好的、简单的自动执行例行数据分发的方法。但不管怎么说,这并不适合每个 Git 存储库。它没有用于高级 Git 功能的接口,它没有暂停按钮或手动管理的操作。没关系,因为它的使用范围是有意限制的。SparkleShare 可以完成它计划要做的事情,它做得很好,而且它是你无需关心的一个 Git 存储库。

如果你想使用这种稳定的、看不见的自动化,请尝试一下 SparkleShare。


via: https://opensource.com/article/19/4/file-sharing-git

作者:Seth Kenlon 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

年末总结一下 Git 的最佳表现。以下是告诉你可以在新的一年中以新奇的方式使用 Git 的十篇文章。

2019 年过去了,是时候回顾一下我们(opensource.com)今年发表的有关 Git 的前 10 篇文章了。

这些文章包括从用例和教程到一些非常有趣的(甚至有些非常规的)使用 Git 的方法。所有这些文章都可以帮助你提高 Git 技能,但是,如果你确实需要 Git 入门介绍,请务必查看我们的《Git 入门:术语入门》文章,并下载我们的 Git 备忘单

  1. Molly de Blanc 写的《GNOME 项目如何使用 Git》考察了 GNOME 项目采用的自托管 GitLab 实例的做法,该实例作为 GNOME 项目集中保存对 GNOME 做出的贡献的位置。De Blanc 从事于非编码职位,因此她的文章还探讨了切换到 GitLab 如何使主要角色不是编写代码的贡献者受益。
  2. Ahmad Awais 写的《Emoji-Log:编写 Git 提交信息的新方法》解释了如何使用 Emoji-Log(一种直白的、开源的 Git 提交日志标准)来编写更好、更用户友好的 Git 提交消息。本文概述了 Emoji-Log 背后的理论以及可添加到 .bashrc.zshrc.gitconfig 中的配置示例,以便你快速开始使用 Emoji-Log。
  3. 在《用 Git 管理你的每日行程》中,Seth Kenlon 解释了如何使用 Git 来管理日历。通过结合 Org 模式、Git 和其他一些工具,Kenlon 描述了如何创建可在设备之间同步的基于 Git 的日历工作流。
  4. 用 Git 作为聊天应用的后端》,Seth Kenlon 探索了另一种使用 Git 的非常规方式。在本文中,他探讨了 GIC,这是一个用 Node.js 编写的聊天客户端,其中 Git 用作后端数据库。GIC 是一个概念验证模型,不是供产品环境使用的东西,但是它提供了一个有趣的示例,说明了使用 Git 的真正独特方法。
  5. Alan Formy-Duval 的《Git 学习实用练习》是一份他为提高 Git 技能而努力的编年史。他分享了自己的经验,以使其他人受益于他所学,而 Git 新手将从这篇对 Git 核心功能的出色概述中受益匪浅。
  6. 用 Git 帮助写作者更好地完成工作》的作者 Seth Kenlon 着眼于写作者如何从基于 Git 的工作流中受益。本文介绍了如何使用 Git 和 Atom 利用 Markdown 进行书写。通过遵循本文中的示例,写作者可以轻松地将复杂的版本控制系统添加到其编写工作流中。
  7. Sachin Patil 在《如何使用 Magit 管理 Git 项目》中,解释了如何使用 Emacs 的 Magit 扩展来管理 Git 存储库。本文提供了有关如何在 Emacs 中执行一系列关键 Git 任务的清晰示例。这些示例带有说明各种任务的屏幕截图。
  8. 把“点文件”放到版本控制中》的作者 Matthew Broberg 演示了如何使用 Git 存储放在主目录中的点文件。将你的点文件存储在 Git 存储库中,可以更轻松地在计算机之间共享系统配置,并且,如果你愿意的话,也可以与其他人共享配置。这篇文章也有不要共享哪些文件的警告,因为共享错误的点文件会将其暴露给其他人。
  9. Austin Dewey 的《4 种用于 Git 加密的机密管理工具》在本文中比较了 Git-crypt、BlackBox、SOPS 和 Transcrypt,研究了在 Git 中存储机密的工具。本文涵盖四个主要领域:支持的加密类型、每个项目的目标、项目的工作流和差异以及其他功能。
  10. Seth Kenlon 的《使用 Git 管理 Git 服务器》解释了如何使用 Gitolite 管理 Git 服务器。Gitolite 是使用 Git 来管理 Git 服务器的“管理杂项的后端应用程序”。本文演示了如何安装、配置和使用 Gitolite,并提供了清晰的示例详细说明了该过程的每个步骤。

via: https://opensource.com/article/19/12/git-resources

作者:Joshua Allen Holm 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

如果你是一名写作者,你也能从使用 Git 中受益。在我们的系列文章中了解有关 Git 鲜为人知的用法。

Git 是一个少有的能将如此多的现代计算封装到一个程序之中的应用程序,它可以用作许多其他应用程序的计算引擎。虽然它以跟踪软件开发中的源代码更改而闻名,但它还有许多其他用途,可以让你的生活更轻松、更有条理。在这个 Git 系列中,我们将分享七种鲜为人知的使用 Git 的方法。

今天我们来看看写作者如何使用 Git 更好的地完成工作。

写作者的 Git

有些人写小说,也有人撰写学术论文、诗歌、剧本、技术手册或有关开源的文章。许多人都在做一些各种写作。相同的是,如果你是一名写作者,或许能从使用 Git 中受益。尽管 Git 是著名的计算机程序员所使用的高度技术性工具,但它也是现代写作者的理想之选,本文将向你演示如何改变你的书写方式以及为什么要这么做的原因。

但是,在谈论 Git 之前,重要的是先谈谈“副本”(或者叫“内容”,对于数字时代而言)到底是什么,以及为什么它与你的交付媒介不同。这是 21 世纪,大多数写作者选择的工具是计算机。尽管计算机看似擅长将副本的编辑和布局等过程结合在一起,但写作者还是(重新)发现将内容与样式分开是一个好主意。这意味着你应该在计算机上像在打字机上而不是在文字处理器中进行书写。以计算机术语而言,这意味着以纯文本形式写作。

以纯文本写作

这个假设曾经是毫无疑问的:你知道自己的写作所要针对的市场,你可以为书籍、网站或软件手册等不同市场编写内容。但是,近来各种市场趋于扁平化:你可能决定在纸质书中使用为网站编写的内容,并且纸质书可能会在以后发布 EPUB 版本。对于你的内容的数字版本,读者才是最终控制者:他们可以在你发布内容的网站上阅读你的文字,也可以点击 Firefox 出色的阅读视图,还可能会打印到纸张上,或者可能会使用 Lynx 将网页转储到文本文件中,甚至可能因为使用屏幕阅读器而根本看不到你的内容。

你只需要逐字写下你的内容,而将交付工作留给发布者。即使你是自己发布,将字词作为写作作品的一种源代码也是一种更聪明、更有效的工作方式,因为在发布时,你可以使用相同的源(你的纯文本)生成适合你的目标输出(用于打印的 PDF、用于电子书的 EPUB、用于网站的 HTML 等)。

用纯文本编写不仅意味着你不必担心布局或文本样式,而且也不再需要专门的工具。无论是手机或平板电脑上的基本的记事本应用程序、计算机附带的文本编辑器,还是从互联网上下载的免费编辑器,任何能够产生文本内容的工具对你而言都是有效的“文字处理器”。无论你身在何处或在做什么,几乎可以在任何设备上书写,并且所生成的文本可以与你的项目完美集成,而无需进行任何修改。

而且,Git 专门用来管理纯文本。

Atom 编辑器

当你以纯文本形式书写时,文字处理程序会显得过于庞大。使用文本编辑器更容易,因为文本编辑器不会尝试“有效地”重组输入内容。它使你可以将脑海中的单词输入到屏幕中,而不会受到干扰。更好的是,文本编辑器通常是围绕插件体系结构设计的,这样应用程序本身很基础(它用来编辑文本),但是你可以围绕它构建一个环境来满足你的各种需求。

Atom 编辑器就是这种设计理念的一个很好的例子。这是一个具有内置 Git 集成的跨平台文本编辑器。如果你不熟悉纯文本格式,也不熟悉 Git,那么 Atom 是最简单的入门方法。

安装 Git 和 Atom

首先,请确保你的系统上已安装 Git。如果运行 Linux 或 BSD,则 Git 在软件存储库或 ports 树中可用。你使用的命令将根据你的发行版而有所不同。例如在 Fedora 上:

$ sudo dnf install git

你也可以下载并安装适用于 MacWindows 的 Git。

你不需要直接使用 Git,因为 Atom 会充当你的 Git 界面。下一步是安装 Atom。

如果你使用的是 Linux,请通过软件安装程序或适当的命令从软件存储库中安装 Atom,例如:

$ sudo dnf install atom

Atom 当前没有在 BSD 上构建。但是,有很好的替代方法,例如 GNU Emacs。对于 Mac 和 Windows 用户,可以在 Atom 网站上找到安装程序。

安装完成后,启动 Atom 编辑器。

快速指导

如果要使用纯文本和 Git,则需要适应你的编辑器。Atom 的用户界面可能比你习惯的更加动态。实际上,你可以将它视为 Firefox 或 Chrome,而不是文字处理程序,因为它具有可以根据需要打开或关闭的选项卡和面板,甚至还可以安装和配置附件。尝试全部掌握 Atom 如许之多的功能是不切实际的,但是你至少可以知道有什么功能。

当打开 Atom 时,它将显示一个欢迎屏幕。如果不出意外,此屏幕很好地介绍了 Atom 的选项卡式界面。你可以通过单击 Atom 窗口顶部选项卡上的“关闭”图标来关闭欢迎屏幕,并使用“文件 > 新建文件”创建一个新文件。

使用纯文本格式与使用文字处理程序有点不同,因此这里有一些技巧,以人可以理解的方式编写内容,并且 Git 和计算机可以解析,跟踪和转换。

用 Markdown 书写

如今,当人们谈论纯文本时,大多是指 Markdown。Markdown 与其说是格式,不如说是样式,这意味着它旨在为文本提供可预测的结构,以便计算机可以检测自然的模式并智能地转换文本。Markdown 有很多定义,但是最好的技术定义和备忘清单在 CommonMark 的网站上。

# Chapter 1

This is a paragraph with an *italic* word and a **bold** word in it.
And it can even reference an image.

![An image will render here.](drawing.jpg)

从示例中可以看出,Markdown 读起来感觉不像代码,但可以将其视为代码。如果你遵循 CommonMark 定义的 Markdown 规范,那么一键就可以可靠地将 Markdown 的文字转换为 .docx、.epub、.html、MediaWiki、.odt、.pdf、.rtf 和各种其他的格式,而不会失去格式。

你可以认为 Markdown 有点像文字处理程序的样式。如果你曾经为出版社撰写过一套样式来控制章节标题及其样式,那基本上就是一回事,除了不是从下拉菜单中选择样式以外,你需要给你的文字添加一些小记号。对于任何习惯“以文字交谈”的现代阅读者来说,这些表示法都是很自然的,但是在呈现文本时,它们会被精美的文本样式替换掉。实际上,这就是文字处理程序在后台秘密进行的操作。文字处理器显示粗体文本,但是如果你可以看到使文本变为粗体的生成代码,则它与 Markdown 很像(实际上,它是更复杂的 XML)。使用 Markdown 可以消除这种代码和样式之间的阻隔,一方面看起来更可怕一些,但另一方面,你可以在几乎所有可以生成文本的东西上书写 Markdown 而不会丢失任何格式信息。

Markdown 文件流行的文件扩展名是 .md。如果你使用的平台不知道 .md 文件是什么,则可以手动将该扩展名与 Atom 关联,或者仅使用通用的 .txt 扩展名。文件扩展名不会更改文件的性质。它只会改变你的计算机决定如何处理它的方式。Atom 和某些平台足够聪明,可以知道该文件是纯文本格式,无论你给它以什么扩展名。

实时预览

Atom 具有 “Markdown 预览” 插件,该插件可以向你显示正在编写的纯文本 Markdown 及其(通常)呈现的方式。

 title=

要激活此预览窗格,请选择“包 > Markdown 预览 > 切换预览” 或按 Ctrl + Shift + M

此视图为你提供了两全其美的方法。无需承担为你的文本添加样式的负担就可以写作,而你也可以看到一个通用的示例外观,至少是以典型的数字化格式显示文本的外观。当然,关键是你无法控制文本的最终呈现方式,因此不要试图调整 Markdown 来强制以某种方式显示呈现的预览。

每行一句话

你的高中写作老师不会看你的 Markdown。

一开始它不那么自然,但是在数字世界中,保持每行一个句子更有意义。Markdown 会忽略单个换行符(当你按下 ReturnEnter 键时),并且只在单个空行之后才会创建一个新段落。

 title=

每行写一个句子的好处是你的工作更容易跟踪。也就是说,假如你在段落的开头更改了一个单词,如果更改仅限于一行而不是一个长的段落中的一个单词,那么 Atom、Git 或任何应用程序很容易以有意义的方式突出显示该更改。换句话说,对一个句子的更改只会影响该句子,而不会影响整个段落。

你可能会想:“许多文字处理器也可以跟踪更改,它们可以突出显示已更改的单个单词。”但是这些修订跟踪器绑定在该字处理器的界面上,这意味着你必须先打开该字处理器才能浏览修订。在纯文本工作流程中,你可以以纯文本形式查看修订,这意味着无论手头有什么,只要该设备可以处理纯文本(大多数都可以),就可以进行编辑或批准编辑。

诚然,写作者通常不会考虑行号,但它对于计算机有用,并且通常是一个很好的参考点。默认情况下,Atom 为文本文档的行进行编号。按下 Enter 键或 Return 键后,一就是一行。

 title=

如果(在 Atom 的)一行的行号中有一个点而不是一个数字,则表示它是上一行折叠的一部分,因为它超出了你的屏幕。

主题

如果你是一个在意视觉形象的人,那么你可能会非常注重自己的写作环境。即使你使用普通的 Markdown 进行编写,也并不意味着你必须使用程序员的字体或在使你看起来像码农的黑窗口中进行书写。修改 Atom 外观的最简单方法是使用主题包。主题设计人员通常将深色主题与浅色主题区分开,因此你可以根据需要使用关键字“Dark”或“Light”进行搜索。

要安装主题,请选择“编辑 > 首选项”。这将在 Atom 界面中打开一个新标签页。是的,标签页可以用于处理文档用于配置及控制面板。在“设置”标签页中,单击“安装”类别。

在“安装”面板中,搜索要安装的主题的名称。单击搜索字段右侧的“主题”按钮,以仅搜索主题。找到主题后,单击其“安装”按钮。

 title=

要使用已安装的主题或根据喜好自定义主题,请导航至“设置”标签页中的“主题”类别中。从下拉菜单中选择要使用的主题。更改会立即进行,因此你可以准确了解主题如何影响你的环境。

你也可以在“设置”标签的“编辑器”类别中更改工作字体。Atom 默认采用等宽字体,程序员通常首选这种字体。但是你可以使用系统上的任何字体,无论是衬线字体、无衬线字体、哥特式字体还是草书字体。无论你想整天盯着什么字体都行。

作为相关说明,默认情况下,Atom 会在其屏幕上绘制一条垂直线,以提示编写代码的人员。程序员通常不想编写太长的代码行,因此这条垂直线会提醒他们不要写太长的代码行。不过,这条竖线对写作者而言毫无意义,你可以通过禁用 “wrap-guide” 包将其删除。

要禁用 “wrap-guide” 软件包,请在“设置”标签中选择“折行”类别,然后搜索 “wrap-guide”。找到该程序包后,单击其“禁用”按钮。

动态结构

创建长文档时,我发现每个文件写一个章节比在一个文件中写整本书更有意义。此外,我不会以明显的语法 chapter-1.md1.example.md 来命名我的章节,而是以章节标题或关键词(例如 example.md)命名。为了将来为自己提供有关如何编写本书的指导,我维护了一个名为 toc.md (用于“目录”)的文件,其中列出了各章的(当前)顺序。

我这样做是因为,无论我多么相信第 6 章都不可能出现在第 1 章之前,但在我完成整本书之前,几乎难以避免我会交换一两个章节的顺序。我发现从一开始就保持动态变化可以帮助我避免重命名混乱,也可以帮助我避免僵化的结构。

在 Atom 中使用 Git

每位写作者的共同点是两件事:他们为流传而写作,而他们的写作是一段旅程。你不能一坐下来写作就完成了最终稿件。顾名思义,你有一个初稿。该草稿会经过修订,你会仔细地将每个修订保存一式两份或三份的备份,以防万一你的文件损坏了。最终,你得到了所谓的最终草稿,但很有可能你有一天还会回到这份最终草稿,要么恢复好的部分,要么修改坏的部分。

Atom 最令人兴奋的功能是其强大的 Git 集成。无需离开 Atom,你就可以与 Git 的所有主要功能进行交互,跟踪和更新项目、回滚你不喜欢的更改、集成来自协作者的更改等等。最好的学习方法就是逐步学习,因此这是在一个写作项目中从始至终在 Atom 界面中使用 Git 的方法。

第一件事:通过选择 “视图 > 切换 Git 标签页” 来显示 Git 面板。这将在 Atom 界面的右侧打开一个新标签页。现在没什么可看的,所以暂时保持打开状态就行。

建立一个 Git 项目

你可以认为 Git 被绑定到一个文件夹。Git 目录之外的任何文件夹都不知道 Git,而 Git 也不知道外面。Git 目录中的文件夹和文件将被忽略,直到你授予 Git 权限来跟踪它们为止。

你可以通过在 Atom 中创建新的项目文件夹来创建 Git 项目。选择 “文件 > 添加项目文件夹”,然后在系统上创建一个新文件夹。你创建的文件夹将出现在 Atom 窗口的左侧“项目面板”中。

Git 添加文件

右键单击你的新项目文件夹,然后选择“新建文件”以在项目文件夹中创建一个新文件。如果你要导入文件到新项目中,请右键单击该文件夹,然后选择“在文件管理器中显示”,以在系统的文件查看器中打开该文件夹(Linux 上为 Dolphin 或 Nautilus,Mac 上为 Finder,在 Windows 上是 Explorer),然后拖放文件到你的项目文件夹。

在 Atom 中打开一个项目文件(你创建的空文件或导入的文件)后,单击 Git 标签中的 “ 创建存储库 Create Repository ” 按钮。在弹出的对话框中,单击 “ 初始化 Init ” 以将你的项目目录初始化为本地 Git 存储库。 Git 会将 .git 目录(在系统的文件管理器中不可见,但在 Atom 中可见)添加到项目文件夹中。不要被这个愚弄了:.git 目录是 Git 管理的,而不是由你管理的,因此一般你不要动它。但是在 Atom 中看到它可以很好地提醒你正在由 Git 管理的项目中工作。换句话说,当你看到 .git 目录时,就有了修订历史记录。

在你的空文件中,写一些东西。你是写作者,所以输入一些单词就行。你可以随意输入任何一组单词,但要记住上面的写作技巧。

Ctrl + S 保存文件,该文件将显示在 Git 标签的 “ 未暂存的改变 Unstaged Changes ” 部分中。这意味着该文件存在于你的项目文件夹中,但尚未提交给 Git 管理。通过单击 Git 选项卡右上方的 “ 暂存全部 Stage All ” 按钮,以允许 Git 跟踪这些文件。如果你使用过带有修订历史记录的文字处理器,则可以将此步骤视为允许 Git 记录更改。

Git 提交

你的文件现在已暂存。这意味着 Git 知道该文件存在,并且知道自上次 Git 知道该文件以来,该文件已被更改。

Git 的 提交 commit 会将你的文件发送到 Git 的内部和永久存档中。如果你习惯于文字处理程序,这就类似于给一个修订版命名。要创建一个提交,请在 Git 选项卡底部的“ 提交 Commit ”消息框中输入一些描述性文本。你可能会感到含糊不清或随意写点什么,但如果你想在将来知道进行修订的原因,那么输入一些有用的信息会更有用。

第一次提交时,必须创建一个 分支 branch 。Git 分支有点像另外一个空间,它允许你从一个时间轴切换到另一个时间轴,以进行你可能想要也可能不想要永久保留的更改。如果最终喜欢该更改,则可以将一个实验分支合并到另一个实验分支,从而统一项目的不同版本。这是一个高级过程,不需要先学会,但是你仍然需要一个活动分支,因此你必须为首次提交创建一个分支。

单击 Git 选项卡最底部的“ 分支 Branch ”图标,以创建新的分支。

 title=

通常将第一个分支命名为 master,但不是必须如此;你可以将其命名为 firstdraft 或任何你喜欢的名称,但是遵守当地习俗有时会使谈论 Git(和查找问题的答案)变得容易一些,因为你会知道有人提到 “master” 时,它们的真正意思是“主干”而不是“初稿”或你给分支起的什么名字。

在某些版本的 Atom 上,UI 也许不会更新以反映你已经创建的新分支。不用担心,做了提交之后,它会创建分支(并更新 UI)。按下 “ 提交 Commit ” 按钮,无论它显示的是 “ 创建脱离的提交 Create detached commit ” 还是 “ 提交到主干 Commit to master

提交后,文件的状态将永久保留在 Git 的记忆之中。

历史记录和 Git 差异

一个自然而然的问题是你应该多久做一次提交。这并没有正确的答案。使用 Ctrl + S 保存文件和提交到 Git 是两个单独的过程,因此你会一直做这两个过程。每当你觉得自己已经做了重要的事情或打算尝试一个可能会被干掉的疯狂的新想法时,你可能都会想要做次提交。

要了解提交对工作流程的影响,请从测试文档中删除一些文本,然后在顶部和底部添加一些文本。再次提交。 这样做几次,直到你在 Git 标签的底部有了一小段历史记录,然后单击其中一个提交以在 Atom 中查看它。

 title=

查看过去的提交时,你会看到三种元素:

  1. 绿色文本是该提交中已被添加到文档中的内容。
  2. 红色文本是该提交中已从文档中删除的内容。
  3. 其他所有文字均未做更改。

远程备份

使用 Git 的优点之一是,按照设计它是分布式的,这意味着你可以将工作提交到本地存储库,并将所做的更改推送到任意数量的服务器上进行备份。你还可以从这些服务器中拉取更改,以便你碰巧正在使用的任何设备始终具有最新更改。

为此,你必须在 Git 服务器上拥有一个帐户。有几种免费的托管服务,其中包括 GitHub,这个公司开发了 Atom,但奇怪的是 GitHub 不是开源的;而 GitLab 是开源的。相比私有软件,我更喜欢开源,在本示例中,我将使用 GitLab。

如果你还没有 GitLab 帐户,请注册一个帐户并开始一个新项目。项目名称不必与 Atom 中的项目文件夹匹配,但是如果匹配,则可能更有意义。你可以将项目保留为私有,在这种情况下,只有你和任何一个你给予了明确权限的人可以访问它,或者,如果你希望该项目可供任何互联网上偶然发现它的人使用,则可以将其公开。

不要将 README 文件添加到项目中。

创建项目后,它将为你提供有关如何设置存储库的说明。如果你决定在终端中或通过单独的 GUI 使用 Git,这是非常有用的信息,但是 Atom 的工作流程则有所不同。

单击 GitLab 界面右上方的 “ 克隆 Clone ” 按钮。这显示了访问 Git 存储库必须使用的地址。复制 “SSH” 地址(而不是 “https” 地址)。

在 Atom 中,点击项目的 .git 目录,然后打开 config 文件。将下面这些配置行添加到该文件中,调整 url 值的 seth/example.git 部分以匹配你自己独有的地址。

[remote "origin"]
  url = [email protected]:seth/example.git
  fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
  remote = origin
  merge = refs/heads/master

在 Git 标签的底部,出现了一个新按钮,标记为 “ 提取 Fetch ”。由于你的服务器是全新的服务器,因此没有可供你提取的数据,因此请右键单击该按钮,然后选择“ 推送 Push ”。这会将你的更改推送到你的 GitLab 帐户,现在你的项目已备份到 Git 服务器上。

你可以在每次提交后将更改推送到服务器。它提供了即刻的异地备份,并且由于数据量通常很少,因此它几乎与本地保存一样快。

撰写而 Git

Git 是一个复杂的系统,不仅对修订跟踪和备份有用。它还支持异步协作并鼓励实验。本文介绍了一些基础知识,但还有更多关于 Git 的文章和整本的书,以及如何使用它使你的工作更高效、更具弹性和更具活力。 从使用 Git 完成小任务开始,使用的次数越多,你会发现自己提出的问题就越多,最终你将学到的技巧越多。


via: https://opensource.com/article/19/4/write-git

作者:Seth Kenlon 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出