加载中

Make your revision-control experience more useful and powerful with these 12 tricks and tips for Git.

Git, the distributed revision-control system that's become the default tool for source code control in the open source world, turns 12 on April 7. One of the more frustrating things about using Git is how much you need to know to use it effectively. This can also be one of the more awesome things about using Git, because there's nothing quite like discovering a new tip or trick that can streamline or improve your workflow.

In honor of Git's 12th birthday, here are 12 tips and tricks to make your Git experience more useful and powerful, starting with some basics you might have overlooked and scaling up to some real power-user tricks!

1. Your ~/.gitconfig file

The first time you tried to use the git command to commit a change to a repository, you might have been greeted with something like this:

*** Please tell me who you are.
Run
  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"
to set your account's default identity.

What you might not have realized is that those commands are modifying the contents of ~/.gitconfig, which is where Git stores global configuration options. There are a vast array of things you can do via your ~/.gitconfig file, including defining aliases, turning particular command options on (or off!) on a permanent basis, and modifying aspects of how Git works (e.g., which diff algorithm git diff uses or what type of merge strategy is used by default). You can even conditionally include other config files based on the path to a repository! See man git-config for all the details.

    使用这12个Git的诀窍与技巧来令你的版本控制经验更加有用。

    Git,一个分布式版本控制系统,它已经成为了开源世界的源码控制默认工具,在4月7号12岁了。但是使用Git中更另人沮丧的是,你需要了解多少才能让你更有效的使用它。同时这也是使用Git中比较美妙的一件事,因为没有什么比发现一个新的小技巧来简化或提高你的工作流的效率更加令人快乐了。

    为了纪念Git的12岁生日,这篇文章提供12个诀窍与技巧来让你的Git经验更加有用和强大,从一些你可能会忽视的基础开始到一些真正的强大技巧!

  1. 你的 ~/.gitconfig 文件

在第一次用git命令来提交一个仓库的修改,你可能会首先看到像下面这种内容:

*** Please tell me who you are.
Run
  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"
to set your account's default identity.


    你可能还没有意识到那些命令正在修改~/.gitconfig文件的内容,这个文件就是Git存储全局配置选项的文件。通过你的~/.gitconfig文件你可要做很多事情,包括定义别名,永久的打开(或关闭)一些特定的命令选项,还可以修改Git如何工作的方面(例如:git diff使用哪个diff算法,或者默认使用什么类型的的合并策略)。你甚至可以按条件地基于路径包含其他配置文件到一个仓库!使用“man git-config”查看所有细节。

2. Your repo's .gitconfig file

In the previous tip, you may have wondered what that --global flag on the git configcommand was doing. It tells Git to update the "global" configuration, the one found in ~/.gitconfig. Of course, having a global config also implies a local configuration, and sure enough, if you omit the --global flag, git config will instead update the repository-specific configuration, which is stored in .git/config.

Options that are set in the .git/config file will override any setting in the ~/.gitconfig file. So, for example, if you need to use a different email address for a particular repository, you can run git config user.email "also_you@example.com". Then, any commits in that repository will use your other email address. This can be super useful if you work on open source projects from a work laptop and want them to show up with a personal email address while still using your work email for your main Git configuration.

Pretty much anything you can set in ~/.gitconfig, you can also set in .git/config to make it specific to the given repository. In any of the following tips, when I mention adding something to your ~/.gitconfig, just remember you could also set that option for just one repository by adding it to .git/config instead.

2. 你的仓库的.gitconfig文件

    在之前的技巧中,你可能会想知道在git config 命令中的—global标识是做什么的。它告诉Git更新“global”配置,也就是~/.gitconfig发现的这个配置。当然,拥有一个全局的配置代表了一个本地配置,而且足够肯定的是,如果你省略—global选项,git config 会更新这个仓库自己的配置,这个配置文件存储在.git/config。
    在.git/config中设置的选项会推翻在~/.gitconfig文件中的对应设置。因此,例如,如果你需要在一个特定的仓库中使用一个不同的邮箱地址,你可以运行“git config user.email "also_you@example.com"”。然后,你在这个仓库中提交会使用你单独配置的这个邮箱地址。如果你使用一个工作的电脑在开源项目中工作,但是希望在这个项目中使用个人的邮箱地址,而其他在主Git配置中仍然使用工作邮箱,这一点是非常有用的。
    在~/.gitconfig中可以设置的任何东西,都可以在.git/config中设置来对这个仓库做特定设置。在下面的这些技巧中,当我提到在你的~/.gitconfig文件中添加什么东西,同时也说明可以在特定的仓库的.git/config中添加来设置那个选项。

3. Aliases

Aliases are another thing you can put in your ~/.gitconfig. These work just like aliases in the command shell—they set up a new command name that can invoke one or more other commands, often with a particular set of options or flags. They're super useful for longer, more complicated commands you use frequently.

You can define aliases using the git config command—for example, running git config --global --add alias.st status will make running git st do the same thing as running git status—but I find when defining aliases, it's frequently easier to just edit the ~/.gitconfig file directly.

If you choose to go this route, you'll find that the ~/.gitconfig file is an INI file. INI is basically a key-value file format with particular sections. When adding an alias, you'll be changing the [alias] section. For example, to define the same git st alias as above, add this to the file:

[alias]
st = status

(If there's already an [alias] section, just add the second line to that existing section.)

3、别名

别名是你可以在你的~/.gitconfig文件里做的另外一件事。他的工作原理就像shell命令行里的别名——设置一个新的命令名称来调用一个或者多个其他的命令,这些命令通常包括一些特定的选项或标识。别名对于你经常使用的那些又长又复杂的命令行是非常有效的。

你可以使用git config命令来定义别名——例如,执行”git config —global —add alias.st status”命令后,会使得执行git st与执行git status做的是同样的事情——然而,我发现当定义别名的时候,只需要直接在~/.gitconfig文件里编辑通常会更加容易。

如果你选择这么做,你会发现~/.gitconfig文件就是一个INI文件,INI是一种带有特定段落的基础键值对文件格式。添加一个别名时,你将改变[alias]段落。例如:上面提到的定义相同的git st别名,需要添加下面这段代码:

[alias]
st = status

(如果已经有了[alias]这个段落,只需要在这个段落中添加到第二行)

4. Aliases to shell commands

Aliases aren't limited to just running other Git subcommands—you can also define aliases that run other shell commands. This is a fantastic way to deal with a recurring, infrequent, and complicated task: Once you've figured out how to do it once, preserve the command under an alias. For example, I have a few repositories where I've forked an open source project and made some local modifications that don't need to be contributed back to the project. I want to keep up-to-date with ongoing development work in the project but also maintain my local changes. To accomplish this, I need to periodically merge the changes from the upstream repo into my fork—which I do by using an alias I call upstream-merge. It's defined like this:

upstream-merge = !"git fetch origin -v && git fetch upstream -v && git merge upstream/master && git push"

The ! at the beginning of the alias definition tells Git to run the command via the shell. This example involves running a number of git commands, but aliases defined in this way can run any shell command.

(Note that if you want to copy my upstream-merge alias, you'll need to make sure you have a Git remote named upstream pointed at the upstream repository you've forked from. You can add this by running git remote add upstream <URL to repo>.)

4. shell命令中的别名

   别名不仅仅是运行其他Git子命令——你也可以定义别名,这些别名可以运行其他shell命令。这是一个很好的方法来处理一个重复的、罕见的、复杂的任务:一旦你已经想到第一次怎么做,那就使用一个别名保存这个命令。例如,我有几个仓库是我fork了一个开源项目,而且在本地做了一些修改,这些修改不用贡献给这个项目。在项目的持续的开发的过程中我想保持最新的版本,同时保留我的本地修改。为了完成这个想法,我需要定期地从upstream仓库中合并这些修改到我的fork——我定义一个别名“upstream-merge”来完成这个操作。定义如下:

upstream-merge = !"git fetch origin -v && git fetch upstream -v && git merge upstream/master && git push"

    别名定义开始的这个“!”是告诉Git来通过shell运行这个命令。这个例子包括了运行一些git命令,但是使用这种方式定义别名可以运行任何shell命令。
    (注意:如果你想复制我的upstream-merge别名,你将需要确认你有一个Git remote命名为upstream来指定这个你fork的upstream仓库。你可以通过“git remote add upstream <URL to repo>”来添加一个。)

5. Visualizing the commit graph

If you work on a project with a lot of branching activity, sometimes it can be difficult to get a handle on all the work that's happening and how it's all related. Various GUI tools allow you to get a picture of different branches and commits in what's called the "commit graph." For example, here's a section of one of my repositories visualized with the GitLab commit graph viewer:
 
   

John Anderson, CC BY

5. 可视化提交图

如果你从事的是一个有很多分支活动的项目,有时可能很难掌握所有正在发生的工作以及它们之间的相关性。各种GUI工具可让你弄清楚不同分支的概况以及在所谓的“提交图”中提交记录。例如,以下是我使用GitLab提交图查看器进行可视化的一个存储卡的部分截图:

 
   

John Anderson, CC BY

If you're a dedicated command-line user or somebody who finds switching tools to be distracting, it's nice to get a similar view of the commit graph from the command line. That's where the --graph argument to the git log command comes in:
 
   

John Anderson, CC BY

This is the same section of the same repo visualized with the following command:

git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative

The --graph option adds the graph to the left side of the log, --abbrev-commit shortens the commit SHAs--date=relative expresses the dates in relative terms, and the --pretty bit handles all the other custom formatting. I have this aliased to git lg, and it is one of my top 10 most frequently run commands.

如果你是专注于命令行的用户,就可以不在多个工具之间切换导致分心,这个工具在命令行上实现了类似图形界面的提交视图。通过 --graph 参数获取 git 的记录:

   

John Anderson, CC BY

下面的命令可以得到一样的仓库可视化片段:

git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative

--graph 选项将图表添加到日志的左侧,--abbrev-commit 存储提交使用了 SHA 方法,          --date=relative 表达式用相对的术语来表示日期,并且  --pretty 以 bit 格式处理自定义格式。我知道 git lg 的别名,它是我最常运行的10个命令之一。

6. A nicer force-push

Sometimes, as hard as you try to avoid it, you'll find that you need to run git push --force to overwrite the history on a remote copy of your repository. You may have gotten some feedback that caused you to do an interactive rebase, or you may simply have messed up and want to hide the evidence.

One of the hazards with force pushes happens when somebody else has made changes on top of the same branch in the remote copy of the repository. When you force-push your rewritten history, those commits will be lost. This is where git push --force-with-lease comes in—it will not allow you to force-push if the remote branch has been updated, which will ensure you don't throw away someone else's work.

6. 更优雅的强制推送(force-push)

有时,就跟你尽量避免使用它一样困难的是,你会发现你需要运行 git push --force 来覆写你仓库的远程副本上的历史记录。你可能已得到了一些反馈,他们会要求你进行交互式的变基(rebase),或者你可能已经搞砸了,并且希望隐藏证据。

当他人在仓库的远程副本的同一分支上进行改动后,会发生强制推送的风险。当你强制推送已重写的历史记录时,某些提交将会丢失。这是 git push --force-with-lease 出现的原因 - 如果远程分支已更新,它不会允许你执行强制推送,这将确保你不会丢弃他人的工作。

7. git add -N

Have you ever used git commit -a to stage and commit all your outstanding changes in a single move, only to discover after you've pushed your commit that git commit -aignores newly added files? You can work around this by using the git add -N (think "notify") to tell Git about newly added files you'd like to be included in commits before you actually commit them for the first time.

8. git add -p

A best practice when using Git is to make sure each commit consists of only a single logical change—whether that's a fix for a bug or a new feature. Sometimes when you're working, however, you'll end up with more than one commit's worth of change in your repository. How can you manage to divide things up so that each commit contains only the appropriate changes? git add --patch to the rescue!

This flag will cause the git add command to look at all the changes in your working copy and, for each one, ask if you'd like to stage it to be committed, skip over it, or defer the decision (as well as a few other more powerful options you can see by selecting ? after running the command). git add -p is a fantastic tool for producing well-structured commits.

7. git add -N

你是否使用过git commit -a在一次行动中提交你所有未完成的修改,只有在你push完你的提交后才发现git commit -a忽略了新添加的文件?解决这个问题你可以用git add -N(“通知”)来告诉Git你想把新添加的文件包含在提交中在你第一次实际提交之前。

8. git add -p

一最佳的实践为当使用Git时确保每个提交只包含一个逻辑更改--不管是修复一个bug还是(实现)一个新功能。然而,有时当你工作,会在你的仓库中出现一个以上的修改提交。你怎么样把事情分开,使每个提交只包含适当的修改呢?git add --patch来解救!

这个标志将会使git add命令查看你工作副本中所有的变更,询问你是否愿意将它提交,跳过,或者推迟决定(还有其他一些更强大的选项,你可以通过在运行这命令后选择?来查看)。git add -p是一个神奇的工具来生产结构良好的提交。

9. git checkout -p

Similar to git add -p, the git checkout command will take a --patch or -p option, which will cause it to present each "hunk" of change in your local working copy and allow you to discard it—basically reverting your local working copy to what was there before your change.

This is fantastic when, for example, you've introduced a bunch of debug logging statements while chasing down a bug. After the bug is fixed, you can first use git checkout -p to remove all the new debug logging, then you git add -p to add the bug fix. Nothing is more satisfying than putting together a beautiful, well-structured commit!

9. git checkout -p

与 git add -p类似,git checkout命令将使用 --patch 或 -p 选项,这会使 git 在本地工作副本中展示每个“大块”的改动,并允许丢弃对应改动 —— 简单地说就是恢复本地工作副本到你改变之前的状态。

某些场景下这非常有用,例如,在你跟踪一个 bug 时引入了一堆调试日志语句,在修正了这个 bug 之后,你可以先使用 git checkout -p 删除所有新加的调试日志,之后使用 git add -p 来添加 bug 修复。没有比组合一个极好的、结构良好的提交更令人满意的了!

10. Rebase with command execution

Some projects have a rule that each commit in the repository must be in a working state—that is, at each commit, it should be possible to compile the code or the test suite should run without failure. This is not too difficult when you're working on a branch over time, but if you end up needing to rebase for whatever reason, it can be a little tedious to step through each rebased commit to make sure you haven't accidentally introduced a break.

Fortunately, git rebase has you covered with the -x or --exec option. git rebase -x <cmd>will run that command after each commit is applied in the rebase. So, for example, if you have a project where npm run tests runs your test suite, git rebase -x npm run testswould run the test suite after each commit was applied during the rebase. This allows you to see if the test suite fails at any of the rebased commits so you can confirm that the test suite is still passing at each commit.

10. Rebase with command execution

有些项目有一条规则,即存储库中的每个提交都必须处于可工作状态 - 也就是说,在每次提交时,代码应该是可编译的,或运行测试套件应该不会失败的。当你在某分支上工作时间长时,但如果你最终因为某种原因需要rebase时,那么跳过每个变基后的提交以确保你没有意外引入一个中断是有些冗长乏味的。

幸运的是,git rebase已经支持了-x或--exec选项。git rebase -x <cmd>将在每次提交应用到rebase后运行该命令。因此,例如,如果你有一个项目,其中npm run tests会运行你的测试套件,那么在rebase期间应用每次提交后,git rebase -x npm run tests将会运行测试套件。这使你可以查看测试套件是否在任何变基后的提交中有失败情况,因此你可以确保测试套件在每次提交时仍能通过。

返回顶部
顶部