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

Chapter 6. Git Tools

阅读更多

1.   Git is smart enough to figure out what commit you meant to type if you provide the first few characters, as long as your partial SHA-1 is at least four characters long and unambiguous—that is, only one object in the current repository begins with that partial SHA-1.

 

2.   If you pass --abbrev-commit to the git log command, the output will use shorter values but keep them unique; it defaults to using seven characters but makes them longer if necessary to keep the SHA-1 unambiguous.

 

3.   If you do happen to commit an object that hashes to the same SHA-1 value as a previous object in your repository, Git will see the previous object in your Git database and assume it was already written. If you try to check out that object again at some point, you'll always get the data of the first object.

 

4.   If a commit has a branch reference pointed at it. Then, you can use a branch name in any Git command that expects a commit object or SHA-1 value.

 

5.   If you want to see which specific SHA a branch points to, you can use a Git plumbing tool called rev-parse :

$ git rev-parse topic1

Ca82a6dff817ec66f44342007202690a93763949

 

6.   One of the things Git does in the background while you're working away is keep a reflog—a log of where your HEAD and branch references have been for the last few months. You can see your reflog by using git reflog . Every time your branch tip is updated for any reason, Git stores that information for you in this temporary history. And you can specify older commits with this data. If you want to see the fifth prior value of the HEAD of your repository, you can use the @{n}   reference that you see in the reflog output:

$ git show HEAD@{5}

You can also use this syntax to see where a branch was some specific amount of time ago:

$ git show master@{yesterday}

 

7.   To see reflog information inline with your normal log information, you can run git log –g . It's important to note that the reflog information is strictly local—it's a log of what you've done in your repository.

 

8.   The other main way to specify a commit is via its ancestry. If you place a ^   at the end of a reference, Git resolves it to mean the parent of that commit. You can also specify a number after the ^ . This syntax is only useful for merge commits, which have more than one parent. The first parent is the branch you were on when you merged, and the second is the commit on the branch that you merged in.

 

9.   The other main ancestry specification is the ˜ . This also refers to the first parent, so HEAD˜ and HEAD^ are equivalent. HEAD˜2   means "the first parent of the first parent," or "the grandparent"—it traverses the first parents the number of times you specify. HEAD~2 are equivalent to HEAD^^ . You can also combine these syntaxes: HEAD˜3^2 .

 

10.   Double-dot syntax basically asks Git to resolve a range of commits that are reachable from one commit but aren't reachable from another. For example :


master..experiment means "all commits reachable by experiment that aren't reachable by master   :

$ git log master..experiemnt

D

C

$ git log experiment..master

F

E

You can also leave off one side of the syntax to have Git assume HEAD . Git substitutes HEAD if one side is missing.

 

11.   Git allows you to use either the ^ character or --not   before any reference from which you don't want to see reachable commits. Thus these three commands are equivalent:

$ git log refA..refB

$ git log ^refA refB

$ git log refB --not refA

If you want to see all commits that are reachable from refA or refB but not from refC , you can type one of these:

$ git log refA refB ^refC

$ git log refA refB --not refC

 

12.   Triple-dot syntax specifies all the commits that are reachable by either of two references but not by both of them. If you want to see what is in master or experiment   but not any common references, you can run

$ git log master...experiment

F

E

D

C

The result appears in the traditional commit date ordering. A common switch to use with the log command in this case is --left-right   , which shows you which side of the range each commit is in

$ git log --left-right master...experiment

< F

< E

> D

> C

 

13.   If you run git add with the -i or --interactive   option, Git goes into an interactive shell mode, displaying something like this:

$ git add −i

           staged       unstaged path

  1:    unchanged          +0/−1 T0D0

  2:    unchanged          +1/−1 index.html

  3:    unchanged          +5/−1 lib/simplegit.rb

 

*** Commands ***

  1: status     2: update        3: revert     4: add untracked

  5:  patch     6: diff          7: quit       8: help

What now>

It lists the changes you've staged on the left and unstaged changes on the right. After this comes a Commands section. Here you can do a number of things, including staging files, unstaging files, staging parts of files, adding untracked files, and seeing diffs of what has been staged. Type “2 ” or “u ” to stage a file. Type “5 or “p ” to stage part of the file, then, for each section of the selected files, it will display hunks of the file diff and ask if you would like to stage them, one by one. To do the partial-file staging—you can also use git add -p or git add --patch on the command line.

 

14.   Stashing takes the dirty state of your working directory—that is, your modified tracked files and staged changes—and saves it on a stack of unfinished changes that you can reapply at any time. If you want to switch branches, but you don't want to commit what you've been working on yet; so you'll stash the changes. To push a new stash onto your stack, run git stash . It will clean the working directory.

 

15.   To see which stashes you've stored, you can use git stash list . You can reapply the one you just stashed by using the command: git stash apply . If you want to apply one of the older stashes, you can specify it by naming it, like this: git stash apply stash@{2} . If you don't specify a stash, Git assumes the most recent stash and tries to apply it.

 

16.   Having a clean working directory and applying the stash on the same branch aren't necessary to successfully apply a stash. You can save a stash on one branch, switch to another branch later, and try to reapply the changes. You can also have modified and uncommitted files in your working directory when you apply a stash—Git gives you merge conflicts if anything no longer applies cleanly.

 

17.   The changes to your files were reapplied, but the file you staged before wasn't restaged. To do that, you must run the git stash apply command with a --index option to tell the command to try to reapply the staged changes.

 

18.   The apply   option only tries to apply the stashed work—you continue to have it on your stack. To remove it, you can run git stash drop with the name of the stash to remove: git stash drop stash@{0} .You can also run git stash pop   to apply the stash and then immediately drop it from your stack.

 

19.   If you stash some work, leave it there for a while, and continue on the branch from which you stashed the work, you may have a problem reapplying the work. If the apply tries to modify a file that you've since modified, you'll get a merge conflict and will have to try to resolve it. If you want an easier way to test the stashed changes again, you can run git stash branch <branch name> , which creates a new branch for you, checks out the commit you were on when you stashed your work, reapplies your work there, and then drops the stash if it applies successfully.

 

20.   If you only want to modify your last commit message, or you want to change the snapshot you committed by adding or changing files, You can run(after you stage the changed or newly added files):

$ git commit -amend

You need to be careful with this technique because amending changes the SHA-1 of the commit. It's like a very small rebase—don't amend your last commit if you've already pushed it.

 

21.  With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish. You can run rebase interactively by adding the -i option to git rebase . You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto.

 

22.   If you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to git rebase -i the parent of the last commit you want to edit, which is HEAD˜2^ or HEAD˜3 :

$ git rebase -i HEAD˜3

Every commit included in the range HEAD˜3..HEAD   will be rewritten, whether you change the message or not. Running this command gives you a list of commits in your text editor that looks something like this:

pick f7f3f6d changed my name a bit

pick 310154e updated README formatting and added blame

pick a5f4a0d added cat-file

The interactive rebase gives you a script that it's going to run. It will start at the commit you specify on the command line (HEAD˜3   ) and replay the changes introduced in each of these commits from top to bottom. It lists the oldest at the top, rather than the newest, because that's the first one it will replay. You need to edit the script so that it stops at the commit you want to edit. To do so, change the word pick to the word edit   for each of the commits you want the script to stop after. When you save and exit the editor, Git replays the commit and stop after the commit you want to edit(rewrite) and drops you on the command line. You can add or change any files and stage them, then you can run git commit –-amend to rewrite that commit. And then you can run git rebase –continue to continue the rebase(replay the follow up commit and stop at the commit you want to edit).

 

23.   If you want to remove the added cat-file   commit and change the order in which the other two commits are introduced, you can change the rebase script from this:

pick f7f3f6d changed my name a bit

pick 310154e updated README formatting and added blame

pick a5f4aOd added cat-file

to this:

pick 310154e updated README formatting and added blame

pick f7f3f6d changed my name a bit

When you save and exit the editor, Git rewinds your branch to the parent of these commits, applies 310154e and then f7f3f6d   , and then stops.

 

24.   If you want to make a single commit from these three commits, you make the script look like this:

pick f7f3f6d changed my name a bit

squash 310154e updated README formatting and added blame

squash a5f4a0d added cat-file

When you save and exit the editor, Git applies all three changes and then puts you back into the editor to merge the three commit messages. When you save that, you have a single commit that introduces the changes of all three previous commits.

 

25.   Suppose you want to split the middle commit of your three commits. Instead of updated README formatting and added blame   , you want to split it into two commits: updated README formatting for the first, and added blame for the second . You can change the instruction on the commit you want to split to edit :

pick f7f3f6d changed my name a bit

edit 310154e updated README formatting and added blame

pick a5f4a0d added cat-file

Then, when the script drops you to the command line, you reset that commit, take the changes that have been reset, and create multiple commits out of them:

$ git reset HEAD^

$ git add README

$ git commit -m 'updated README formatting'

$ git add lib/simplegit.rb

$ git commit -m 'added blame'

$ git rebase -continue

 

26.  To remove a file named passwords.txt from your entire history, you can use the --tree-filter option to filter-branch :

$ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD

The --tree-filter   option runs the specified command after each checkout of the project and then recommits the results. In this case, you remove a file called passwords.txt   from every snapshot, whether it exists or not. To run filter-branch   on all your branches, you can pass --all to the command.

 

27.  Suppose you've done an import from another source control system and have subdirectories that make no sense (trunk , tags , and so on). If you want to make the trunk subdirectory be the new project root for every commit:

$ git filter-branch --subdirectory-filter trunk HEAD

Now your new project root is what was in the trunk subdirectory each time. Git will also automatically remove commits that didn't affect the subdirectory.

 

28.  If you forgot to run git config   to set your name and e-mail address before you started working, or perhaps you want to open-source a project at work and change all your work e-mail addresses to your personal address. In any case, you can change e-mail addresses in multiple commits in a batch with filter-branch   as well:

$ git filter-branch --commit-filter '

        if [ "$GIT_AUTHOR_EMAIL" = "schaconglocalhost" ];

        then

                 GIT_AUTHOR_NAME="Scott Chacon";

                GIT_AUTHOR_EMAIL="schacon@example.com";

                git commit-tree "$@"

        else

                git commit-tree "$@"

        fi' HEAD

Because commits contain the SHA-1 values of their parents, this command changes every commit SHA in your history, not just those that have the matching e-mail address.

 

29.   File annotation shows you what commit was the last to modify each line of any file. So, if you see that a method in your code is buggy, you can annotate the file with git blame   to see when each line of the method was last edited and by whom. The following example uses the -L   option to limit the output to lines 12 through 22:

$ git blame -L 12,22 simplegit.rb

^4832fe2 (Scott Chacon  2008-03-15 10:31:28 0700 12) def show(tree = 'master')

^4832fe2 (Scott Chacon  2008-03-15 10:31:28 0700 13)  command("git show #{tree}")

^4832fe2 (Scott Chacon   2008-03-15 10:31:28 0700 14) end

^4832fe2 (Scott Chacon  2008-03-15 10:31:28 0700 15)

9f6560e4 (Scott Chacon  2008-03-17 21:52:20 0700 16) def log(tree = 'master')

79eaf55d (Scott Chacon  2008-04-06 10:15:08 0700 17)  command("git log #{tree}")

9f6560e4 (Scott Chacon  2008-03-17 21:52:20 0700 18) end

9f6560e4 (Scott Chacon  2008-03-17 21:52:20 0700 19)

42cf286l (Magnus Chacon 2008-04-13 10:45:01 0700 20) def blame(path)

42cf286l (Magnus Chacon 2008-04-13 10:45:01 0700 21)  command("git blame #{path}")

42cf286l (Magnus Chacon 2008-04-13 10:45:01 0700 22) end

The first field is the partial SHA-1 of the commit that last modified that line. The next two fields are the author name and the authored date of that commit. After that come the line number and the content of the file. The “^” before the partial SHA-1 designate that commit is when this file was first added to this project, and those lines have been unchanged since.

 

30.   You can ask Git to figure out all sorts of code movement as well. If you pass -C to git blame   , Git analyzes the file you're annotating and tries to figure out where snippets of code within it originally came from if they were copied from elsewhere.

 

 

31.  The bisect   command does a binary search through your commit history to help you identify as quickly as possible which commit introduced an issue. You run git bisect start to get things going, and then you use git bisect bad   to tell the system that the current commit you're on is broken. Then, you must tell bisect   when the last known good state was, using git bisect good [good_commit] :

$ git bisect start

$ git bisect bad

$ git bisect good v1.0

Bisecting: 6 revisions left to test after this

[ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo

Git figured out that about 12 commits came between the commit you marked as the last good commit (v1.0) and the current bad version, and it checked out the middle one for you. At this point, you can run your test to see if the issue exists as of this commit. Suppose it turns out there is no issue here, and you tell Git that by typing git bisect good and continue your journey:

$ git bisect good

Bisecting: 3 revisions left to test after this

[b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing

Now you're on another commit, halfway between the one you just tested and your bad commit. You run your test again and find that this commit is broken, so you tell Git that with git bisect bad :

$ git bisect bad

Bisecting: 1 revisions left to test after this

[f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table

Suppose this commit is fine, and now Git has all the information it needs to determine where the issue was introduced. It tells you the SHA-1 of the first bad commit and shows some of the commit information and which files were modified in that commit so you can figure out what happened that may have introduced this bug.

When you're finished, you should run git bisect reset to reset your HEAD to where you were before you started, or you'll end up in a weird state.

 

32.   If you have a script that will exit 0 if the project is good or non-0 if the project is bad, you can fully automate git bisect   . First, you again tell it the scope of the bisect by listing the known bad commit first and the known good commit second with the bisect start   command:

$ git bisect start HEAD v1.O

$ git bisect run test-error.sh

 

33.   Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate.

 

34.   You add external projects as submodules with the git submodule add command:

$ git submodule add git://github.com/chneukirchen/rack.git rack

If you run git status   right after you add the submodule, you see two things in the staging area: .gitmodules   file and rack file. .gitmodules is a configuration file that stores the mapping between the project's URL and the local subdirectory you've pulled it into:

$ cat .gitmodules

[submodule "rack"]

      path = rack

      url = git://github.com/chneukirchen/rack.git

This file is version-controlled with your other files, like your .gitignore file. It's pushed and pulled with the rest of your project. This is how other people who clone this project know where to get the submodule projects from.

Although rack is a subdirectory in your working directory, Git sees it as a submodule and doesn't track its contents when you're not in that directory. Instead, Git records it as a particular commit from that repository. When you make changes and commit in that subdirectory, the superproject notices that the HEAD   there has changed and records the exact commit you're currently working off of; that way, when others clone this project, they can re-create the environment exactly.

 

35.   You can treat the submodule as a separate project and then update your superproject from time to time with a pointer to the latest commit in that subproject. All the Git commands work independently in the two directories.

 

36.   If you clone a project with a submodule in it, you get the directories that contain submodules, but none of the files yet. You must run two commands: git submodule init to initialize your local configuration file, and git submodule update   to fetch all the data from that project and check out the appropriate commit listed in your superproject.

 

37.   If another developer makes changes to the submodule code and commits, you pull that reference down and merge it in. Here what you merged in is basically a change to the pointer for your submodule; but it doesn't update the code in the submodule directory. This is the case because the pointer you have for the submodule isn't what is actually in the submodule directory. To fix this, you must run git submodule update again.

 

38.   When Git merges, it looks at what it has to merge together and then chooses an appropriate merging strategy to use. If you're merging two branches, Git uses a recursive strategy. If you're merging more than two branches, Git picks the octopus strategy. These strategies are automatically chosen for you because the recursive strategy can handle complex three-way merge situations—for example, more than one common ancestor—but it can only handle merging two branches. The octopus merge can handle multiple branches but is more cautious to avoid difficult conflicts, so it's chosen as the default strategy if you're trying to merge more than two branches.

 

39.   The idea of the subtree merge is that you have two projects, and one of the projects maps to a subdirectory of the other one or vice versa. When you specify a subtree merge, Git is smart enough to figure out that one is a subtree of the other and merge appropriately. If you have Rack project in your rack_branch branch and your own project in the master branch. You want to pull the Rack project into your master project as a subdirectory. You can do that in Git with git read-tree :

$ git read-tree --prefix=rack/ -u rack_branch

It reads the root tree of rack_branch branch into your current index and working directory. You pull the rack_branch branch into the rack subdirectory of your master branch main project. If the Rack project updates, you can pull in upstream changes by switching to that branch and pulling. Then, you can merge those changes back into your master branch. You can use git merge -s subtree and it will work fine; but Git will also merge the histories together, which you probably don't want. To pull in the changes and prepopulate the commit message, use the --squash and --no-commit options as well as the -s subtree strategy option:

$ git checkout master

$ git merge --squash -s subtree --no-commit rack_branch

All the changes from your Rack project are merged in and ready to be committed locally. You can also do the opposite—make changes in the rack subdirectory of your master branch and then merge them into your rack-branch   branch.

To get a diff between what you have in your rack subdirectory and the code in your rack_branch branch—to see if you need to merge them—you must run git diff-tree with the branch you want to compare to:

$ git diff-tree -p rack_branch

  • 大小: 12.6 KB
分享到:
评论

相关推荐

    Git.Mastering.Version.Control.pdf

    Learn everything you need to take ...Chapter 6. Migrating to Git Chapter 7. Git Resources Chapter 8. Recovering from Mistakes Chapter 9. Repository Maintenance Chapter 10. Patching and Offline Sharing

    Learn.Git.in.a.Month.of.Lunches.1617

    Chapter 6 Tracking and updating files in Git Chapter 7 Committing parts of changes Chapter 8 The time machine that is Git Chapter 9 Taking a fork in the road Chapter 10 Merging branches Chapter 11 ...

    Professional.Git.epub

    Git works with the most popular software development tools and is used by almost all of the major technology companies. More than 40 percent of software developers use it as their primary source ...

    Manning.Git.in.Practice.2014.9.pdf

    **Chapter 6: Rewriting History and Disaster Recovery** - **Rewriting History**: Using commands like `git reset` and `git rebase`, you can rewrite parts of the commit history. This should be done ...

    Building.Tools.with.GitHub.Customize.Your.Workflow.epub

    Chapter 6. Ruby and Jekyll Chapter 7. Android and the Git Data API Chapter 8. CoffeeScript, Hubot, and the Activity API Chapter 9. JavaScript and the Git Data API Appendix A. GitHub Enterprise ...

    Bioinformatics.Data.Skills.Reproducible.and.Robust.Research.pdf

    Chapter 6. Bioinformatics Data Part III. Practice: Bioinformatics Data Skills Chapter 7. Unix Data Tools Chapter 8. A Rapid Introduction to the R Language Chapter 9. Working with Range Data Chapter ...

    Moodle.3.Administration.3rd.Edition.1783289716

    Chapter 6. Managing Permissions – Roles and Capabilities Chapter 7. Moodle Look and Feel Chapter 8. Moodle Plugins Chapter 9. Moodle Configuration Chapter 10. Moodle Logging and Reporting Chapter 11....

    Continuous.Integration.Delivery.and.Deployment.epub

    Chapter 6. Automation With Gulp Chapter 7. Automation With Jenkins Chapter 8. A Nodejs And Mongodb Web App Chapter 9. A C# .Net Core And Postgresql Web App Chapter 10. Additional Jenkins Plugins ...

    Introducing.GitHub.A.Non-Technical.Guide

    Title: Introducing GitHub: A Non-Technical Guide Author: Brent Beer, Peter Bell Length: 142 pages Edition: 1 Language: English Publisher: O'Reilly Media ...Chapter 6. Downloading Chapter 7. Next Steps

    GitHub.Essentials.1783553715

    Unleash the power of collaborative development workflow using GitHub, one step at a time About This Book Effectively use GitHub by learning its ...Chapter 6. Exploring the User and Repository Settings

    Deep Learning Cookbook

    In Chapter 1 we’ll look at a variety of options, and throughout the book the example code for each chapter will usually show in the first recipe how to get the needed training data. At the same time...

    Laraboot: Laravel 5 For Beginners

    Git . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Command Console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 ...

    The way to go

    Chapter 3—Editors, IDE’s and Other tools.........................................................................28 3.1 Basic requirements for a decent Go development environment......................

    [Go语言入门(含源码)] The Way to Go (with source code)

    Chapter 3—Editors, IDE’s and Other tools.........................................................................28 3.1 Basic requirements for a decent Go development environment......................

    iOS.10.in.Swift.3.epub

    Chapter 6: Loops Chapter 7: Dictionaries Chapter 8: Boolean Logic & Conditional Statements Chapter 9: Math Operators Chapter 10: Classes Chapter 11: Inheritance Chapter 12: Polymorphism Chapter 13: ...

    unix power tools 3ed.pdf

    **2.4 X 环境 (Chapter 6: Your X Environment)** - **桌面环境**: 如GNOME, KDE等的特点和设置方法。 - **X 窗口管理器**: 如ratpoison等的使用技巧。 - **图形界面应用**: 在Unix环境下安装和使用图形界面软件的...

Global site tag (gtag.js) - Google Analytics