您的位置:首页 > 运维架构 > 网站架构

开源架构的利弊分析

2016-04-02 13:02 831 查看
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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: