`
leonzhx
  • 浏览: 769251 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Chapter 7. Customizing Git

阅读更多

1.   Git uses a series of configuration files to determine non-default behavior that you may want. The first place Git looks for these values is in an /etc/gitconfig file which contains values for every user on the system and all of their repositories. If you pass the option --system to git config   , it reads and writes from this file specifically. The next place Git looks is the ˜/.gitconfig   file, which is specific to each user. You can make Git read and write to this file by passing the --global option. Finally, Git looks for configuration values in the config file in the Git directory (.git/config ) of whatever repository you're currently using. These values are specific to that single repository. Each level overwrites values in the previous level.

 

2.  The configuration options recognized by Git fall into two categories: client side and server side. If you want to see a list of all the options your version of Git recognizes, you can run:

$ git config --help

 

3.   By default, Git uses whatever you've set as your shell default text editor or else falls back to the Vi editor to create and edit your commit and tag messages. To change that default to something else, you can use the core.editor setting:

$ git config --global core.editor emacs

 

4.   To tell Git to use a file as the default message that appears in your editor when you run git commit , set the commit.template configuration value:

$ git config --global commit.template $HOME/.gitmessage.txt

 

5.   The core.pager setting determines what pager is used when Git pages output such as log and diff. You can set it to more or to your favorite pager (by default, it's less ), or you can turn it off (disable paging) by setting it to a blank string:

$ git config --global core.pager ''

 

6.   If you're making signed annotated tags , setting your GPG signing key as a configuration setting makes things easier:

$ git config --global user.signingkey <gpg-key-id>

Now, you can sign tags without having to specify your key every time with the git tag command:

$ git tag -s <tag-name>

 

7.   You can put patterns in your project's .gitignore   file to have Git not see them as untracked files if you want another file outside of your project to hold those values or have extra values, you can tell Git where that file is with the core.excludesfile   setting. Simply set it to the path of a file that has content similar to what a .gitignore   file would have.

 

8.   If you set help.autocorrect to 1   , Git will automatically run the uncompleted command if it has only one match under this scenario. (Git 1.6.1 and above)

 

9.   To turn on all the default terminal coloring, set color.ui to true . When that value is set, Git colors its output if the output goes to a terminal. The other settings are false and always   , which sets colors all the time, even if you're redirecting Git commands to a file or piping them to another command. Always is seldom used, you can instead pass a --color   flag to the Git command to force it to use color codes.(Git 1.5.5 and above)

 

10.   Git provides verb-specific coloring settings. Each of these can be set to true , false   , or always :

color.branch

color.diff

color.interactive

color.status

Each of these has subsettings you can use to set specific colors for parts of the output. To set the meta information in your diff output to blue foreground, black background, and bold text, you can run:

$ git config --global color.diff.meta "blue black bold"

See the git config   manpage for all the subsettings you can configure.

 

11.   Visual Merge Tool (P4Merge) is a graphical merge conflict-resolution tool which you can download here:

http://www.perforce.com/perforce/downloads/component.html

 

12.   To use your custom merge resolution and diff tools, You need to first prepare your merge and diff command wrapper:

$ cat /usr/local/bin/extMerge

#!/bin/sh/Applications/p4merge.app/Contents/MacOS/p4merge $*

$ cat /usr/local/bin/extDiff

#!/bin/sh

[ $# -eq 7 ] && /usr/local/bin/extMerge "$2" "$5"

Git passes the following arguments to the diff program:

path old-file old-hex old-mode new-file new-hex new-mode

You only want the old-file and new-file   arguments.

Then you make these two wrapper executable and run:

$ git config --global merge.tool extMerge

$ git config --global mergetool.extMerge.cmd \

    'extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"'

$ git config --global mergetool.trustExitCode false

$ git config --global diff.external extDiff

or you can edit your ˜/.gitconfig file to add these lines:

[merge]

  tool = extMerge

[mergetool "extMerge"]

  cmd = extMerge "$BASE" "$LOCAL" "$REMOTE" "$MERGED"

  trustExitCode = false

[diff]

  external = extDiff

merge.tool to tell Git what strategy to use, mergetool.*.cmd to specify how to run the command, mergetool.trustExitCode   to tell Git if the exit code of that program indicates a successful merge resolution or not, and diff.external   to tell Git what command to run for diffs.

If you run diff commands, Git fires up P4Merge. If you try to merge two branches and subsequently have merge conflicts, you can run the command git mergetool   ; it starts P4Merge to let you resolve the conflicts through that GUI tool. The nice thing about this wrapper setup is that you can change your diff and merge tools easily.

 

13.   If you're programming on Windows, you'll probably run into line-ending issues at some point. Because Windows uses both a carriage-return character and a linefeed character for newlines in its files, whereas Mac and Linux systems use only the linefeed character. Git can handle this by auto-converting CRLF line endings into LF when you commit, and vice versa when it checks out code onto your file system. You can turn on this functionality with the core.autocrlf setting. If you're on a Windows machine, set it to true   —this converts LF endings into CRLF when you check out code.

If you're on a Linux or Mac system, then you don't want Git to automatically convert LF when you check out files. However, if a file with CRLF endings accidentally gets introduced, then you may want Git to fix it. You can tell Git to convert CRLF to LF on commit but not the other way around by setting core.autocrlf to input .

 

14.   Git by default enables fix for trailing-space , which looks for spaces at the end of a line, and space-before-tab , which looks for spaces before tabs at the beginning of a line.

Git by default disables fix for indent-with-non-tab   , which looks for lines that begin with eight or more spaces instead of tabs, and cr-at-eol , which tells Git that carriage returns at the end of lines are OK. You can tell Git which of these you want enabled by setting core.whitespace   to the values you want on or off, separated by commas. Git will detect these issues when you run a git diff command and try to color them so you can possibly fix them before you commit. When you're applying patches, you can ask Git to warn you if it's applying patches with the specified whitespace issues:

$ git apply --whitespace=warn <patch>

Or you can have Git try to automatically fix the issue before applying the patch:

$ git apply --whitespace=fix <patch>

 

15.   If you want Git to check object consistency on every push, you can force it to do so by setting receive.fsckObjects to true . Git will then check to make sure each object still matches its SHA-1 checksum and points to valid objects.(server side config)

 

16.   To disable the ability to force-update remote branches to non-fast-forward references:

$ git config --system receive.denyNonFastForwards true

(server side config)

 

17.   One of the workarounds to the denyNonFastForwards policy is for the user to delete the branch and then push it back up with the new reference. You can set receive.denyDeletes to true to avoid it. (Git 1.6.1 and above, server side config)

 

18.   Some of these settings can also be specified for a path, so that Git applies those settings only for a subdirectory or subset of files. These path-specific settings are called Git attributes and are set either in a .gitattribute   file in one of your directories or in the .git/info/attributes file if you don't want the attributes file committed with your project. Using attributes, you can do things like specify separate merge strategies for individual files or directories in your project, tell Git how to diff non-text files, or have Git filter content before you check it into or out of Git.

 

19.   To tell Git to treat all pbxproj files as binary data, add the following line to your .gitattributes file:

*.pbxproj -crlf -diff

Now, Git won't try to convert or fix CRLF issues; nor will it try to compute or print a diff for changes in this file when you run git show or git diff on your project. In the 1.6 series of Git, you can also use a macro that is provided that means -crlf -diff :

*.pbxproj binary

 

20.   Put the following line in your .gitattributes file:

*.doc diff= wor d

This tells Git that any file that matches this pattern (.doc ) should use the "word " filter when you try to view a diff that contains changes. You can setup the “word ” filter by:

$ git config diff.word.textconv strings

This configures Git to use the strings   program to convert Word documents into readable text files. Now Git knows that if it tries to do a diff between two snapshots, and any of the files end in .doc   , it should run those files through the "word " filter, which is defined as the strings program. strings   is available on most Mac and Linux systems, so it may be a good first try to do this with many binary formats.

 

21.   If you download and install the exiftool   program, you can use it to convert your images into text about the metadata.

 

22.   You can inject text into a file when it's checked out and remove it again before it's added to a commit. Git attributes offers you two ways to do this. You can inject the SHA-1 checksum of a blob into an $Id$   field in the file automatically. It's important to notice that it isn't the SHA of the commit, but of the blob itself:

$ echo '*.txt ident' >> .gitattributes

$ echo '$Id$' > test.txt

$ git add test.txt

$ git commi t

$ rm text.txt

$ git checkout -- text.txt

$ cat test.txt

$Id: 42812b7653c7b88933f8a9d6cad0ca16714b9bb3 $

 

23.   You can write your own filters for doing substitutions in files on commit/checkout. These are the "clean " and "smudge " filters. In the .gitattributes file, you can set a filter for particular paths and then set up scripts that will process files just before they're committed and just before they're checked out:



 

You can set the filter attribute in your .gitattributes :

*.c     filter=indent

Then, define the “intent ” filter: (tell Git what it does on smudge and clean ):

$ git config --global filter.indent.clean indent

$ git config --global filter.indent.smudge cat

 

24.   Another example of filter is to replace $Date$ with the commit date when git checkout and roll back the changes when git add . You can set your smudge script in ruby:

#! /usr/bin/env ruby

data = STDIN.read

last_date = 'git log --pretty=format:"%ad" 1'

puts data.gsub('$Date$', '$Date: ' + last_date.to_s + '$')

And your clean script can be a simple perl script:

$ git config filter.dater.smudge expand_date

$ git config filter.dater.clean 'perl -pe "s/\\\$Date[^\\\$]*\\\$/\\\$Date\\\$/"'

Then you can test it:

$ echo '# $Date$' > date_test.txt

$ echo 'date*.txt filter=dater' >> .gitattributes

$ git add date_test.txt .gitattributes

$ git commit -m "Testing date expansion in Git"

$ rm date_test.txt

$ git checkout date_test.txt

$ cat date_test.txt

# $Date: Tue Apr 21 07:26:52 2009 0700$

 

25.   If you have some test files in a test/   subdirectory, and it doesn't make sense to include them in the tarball export of your project. You can add the following line to your Git attributes file:

test/ export-ignore

Now, when you run git archive to create a tarball of your project, that directory won't be included in the archive.

 

26.  G it lets you put the string $Format:$ in any file with any of the --pretty=format   formatting shortcodes. For instance, if you want to include a file named LAST_COMMIT   in your project, and the last commit date was automatically injected into it when git archive ran, you can:

$ echo 'Last commit date: $Format:%cd$' > LAST_COMMIT

$ echo "LAST_COMMIT export-subst" >> .gitattributes

$ git add LAST_COMMIT .gitattributes

$ git commit -am 'adding LAST_COMMIT file for archives'

 

27.   If you have a database settings file called database.xml that is different in two branches, and you want to merge in your other branch without messing up the database file. You can set up an attribute like this:

database.xml merge=ours

If you merge in the other branch, instead of having merge conflicts with the database.xml file, you see something like this:

$ git merge topic

Auto-merging database.xml

Merge made by recursive.

 

28.   Git has a way to fire off custom scripts when certain important actions occur. There are two groups of these hooks: client side and server side. The client-side hooks are for client operations such as committing and merging. The server-side hooks are for Git server operations such as receiving pushed commits.

 

29.  T he hooks are all stored in the hooks subdirectory of the Git directory. In most projects, that's .git/hooks . Git populates this directory with a bunch of example scripts. For post-1.6 versions of Git, these example hook files end with .sample ; you'll need to rename them. For pre-1.6 versions of Git, the example files are named properly but are not executable. To enable a hook script, put a file in the hooks   subdirectory of your Git directory that is named appropriately and is executable.

 

30.   The pre-commit hook is run first, before you even type in a commit message. It's used to inspect the snapshot that's about to be committed. Exiting non-zero from this hook aborts the commit, although you can bypass it with git commit --no-verify   . You can do things like check for code style (run lint   ), check for trailing whitespace (the default hook does exactly that), or check for appropriate documentation on new methods.

 

31.   The prepare-commit-msg   hook is run before the commit message editor is fired up but after the default message is created. It lets you edit the default message before the commit author sees it. This hook takes a few options: the path to the file that holds the commit message so far, the type of commit, and the commit SHA-1 if this is an amended commit. It's good for commits where the default message is auto-generated, such as templated commit messages, merge commits, squashed commits, and amended commits. You may use it in conjunction with a commit template to programmatically insert information.

 

32.   The commit-msg   hook takes one parameter, which again is the path to a temporary file that contains the current commit message. If this script exits non-zero, Git aborts the commit process, so you can use it to validate your project state or commit message(whether it’s conformant to a required pattern) before allowing a commit to go through.

 

33.   After the entire commit process is completed, the post-commit hook runs. It doesn't take any parameters, but you can easily get the last commit by running git log −1 HEAD   . Generally, this script is used for notification or something similar.

 

34.   The client-side hooks are often used to enforce certain policies, and these scripts aren't transferred during a clone. You can enforce policy on the server side to reject pushes of commits that don't conform to some policy, but it's entirely up to the developer to use these scripts on the client side.

 

35.   You can set up three client-side hooks for an e-mail-based workflow. They're all invoked by the git am   command. If you're taking patches over e-mail prepared by git format-patch , then some of these may be helpful to you. applypatch-msg takes a single argument: the name of the temporary file that contains the proposed commit message. Git aborts the patch if this script exits non-zero. You can use this to make sure a commit message is properly formatted or to normalize the message by having the script edit it in place. pre-applypatch   takes no arguments and is run after the patch is applied, so you can use it to inspect the snapshot before making the commit. You can run tests or otherwise inspect the working tree with this script. If something is missing or the tests don't pass, exiting non-zero also aborts the git am script without committing the patch. You can use post-applypatch to notify a group or the author of the patch you pulled in that you've done so. You can't stop the patching process with this script.

 

36.   The pre-rebase   hook runs before you rebase anything and can halt the process by exiting non-zero. You can use this hook to disallow rebasing any commits that have already been pushed. The example pre-rebase hook that Git installs does this, although it assumes that next is the name of the branch you publish.

 

37.   After you run a successful git checkout , the post-checkout   hook runs; you can use it to set up your working directory properly for your project environment. This may mean moving in large binary files that you don't want source controlled, auto-generating documentation, or something along those lines.

 

38.   The post-merge hook runs after a successful merge command. You can use it to restore data in the working tree that Git can't track, such as permissions data. This hook can likewise validate the presence of files external to Git control that you may want copied in when the working tree changes.

 

39.   pre-receive takes a list of references that are being pushed from stdin ; if it exits non-zero, none of them are accepted. You can use this hook to do things like make sure none of the updated references are non-fast-forwards; or to check that the user doing the pushing has create, delete, or push access or access to push updates to all the files they're modifying with the push.

 

40.   The post-receive   hook runs after the entire process is completed and can be used to update other services or notify users. It takes the same stdin data as the pre-receive   hook. This script can't stop the push process, but the client doesn't disconnect until it has completed; so, be careful when you try to do anything that may take a long time.

 

41.   The update hook is very similar to the pre-receive   hook, except that it's run once for each branch the pusher is trying to update. If the pusher is trying to push to multiple branches, pre-receive runs only once, whereas update   runs once per branch they're pushing to. Instead of reading from stdin , this script takes three arguments: the name of the reference (branch), the SHA-1 that reference pointed to before the push, and the SHA-1 the user is trying to push. If the update script exits non-zero, only that reference is rejected; other references can still be updated.

 

42.   All the sample hook scripts distributed with Git are in either Perl or Bash scripting, so you can also see plenty of examples of hooks in those languages by looking at the samples.

 

43.   git rev-list is basically the git log   command, but by default it prints out only the SHA-1 values and no other information. So, to get a list of all the commit SHAs introduced between one commit SHA and another, you can run something like this:

$ git rev-list 538c33..d14fc7

 

44.  T o get the raw commit data, you can use another plumbing command called git cat-file :

$ git cat-file commit ca82a6

tree Cfda3bf379e4f8dba8717dee55aab78aef7f4daf

parent 085bb3bcb608ele8451d4b2432f8ecbe6306e7e7

author Scott Chacon <schacon@gmail.com> 1205815931 −0700

committer Scott Chacon <schacon@gmail.com> 1240030591 −0700

 

changed the version number

 

A simple way to get the commit message from a commit when you have the SHA-1 value is to go to the first blank line and take everything after that. You can do so with the sed command on Unix systems:

$ git cat-file commit ca82a6 | sed '1,/^$/d'

changed the version number

 

45.   You can pretty easily see what files have been modified in a single commit with the --name-only option to the git log command:

$ git log 1 --name-only --pretty=format:'' 9f585d

 

README

lib/test.rb

 

46.   It's important to note that anything your update hook script prints to stdout will be transferred to the client.

 

47.   Client-side hook scripts run from your working directory, while server-side hook scripts run from your Git directory.

 

48.   The SHA^@   syntax resolves to all the parents of that commit.

git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}

The above command is looking for any commit that is reachable from the last commit on the remote and that isn't reachable from any parent of any of the SHAs.

  • 大小: 48.9 KB
  • 大小: 47.2 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics