Version Control - CL1 Flashcards

1
Q

Fundamental concepts: revisions, working copy, repository

A

Getting a Git Repository
You can get a Git project using two main approaches. The first takes an existing project or directory and imports it into Git. The second clones an existing Git repository from
another server.

Initializing a Repository in an Existing Directory
If you’re starting to track an existing project in Git, you need to go to the project’s directory and type
$ git init
This creates a new subdirectory named .git that contains all of your necessary repository files — a Git repository skeleton. At this point, nothing in your project is tracked
yet.
If you want to start version-controlling existing files (as opposed to an empty directory), you should probably begin tracking those files and do an initial commit. You can
accomplish that with a few git add commands that specify the files you want to track, followed by a commit:
$ git add *.c
$ git add README
$ git commit m ’initial project version’

Revision Selection
Git allows you to specify specific commits or a range of commits in several ways. They aren’t necessarily obvious but are helpful to know.

Single Revisions
You can obviously refer to a commit by the SHA–1 hash that it’s given, but there are more human-friendly ways to refer to commits as well.

Short SHA
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.

Branch References
The most straightforward way to specify a commit requires that it have 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. For instance, if you want to show the last commit object on a branch, the following commands are equivalent, assuming that the topic1
branch points to ca82a6d :
$ git show ca82a6dff817ec66f44342007202690a93763949
$ git show topic1

RefLog Shortnames
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.

Ancestry References
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.
$ git show HEADˆ
You can also specify a number after the ˆ — for example, d921970ˆ2 means “the second parent of d921970.” This syntax is only useful for merge commits, which have
more than one parent.

Commit Ranges
Now that you can specify individual commits, let’s see how to specify ranges of commits. This is particularly useful for managing your branches — if you have a lot of branches, you can use range specifications to answer questions such as, “What work is on this branch that I haven’t yet merged into my main branch?”
You want to see what is in your experiment branch that hasn’t yet been merged into your master branch. You can ask Git to show you a log of just those commits with
master..experiment — that means “all commits reachable by experiment that aren’t reachable by master.” For the sake of brevity and clarity in these examples, I’ll use the letters of the commit objects from the diagram in place of the actual log output in the order that they would display:
$ git log master..experiemnt

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Getting (checking out) data from repository

A

Cloning an Existing Repository
If you want to get a copy of an existing Git repository — for example, a project you’d like to contribute to — the command you need is git clone. If you’re familiar with
other VCS systems such as Subversion, you’ll notice that the command is clone and not checkout. This is an important distinction — Git receives a copy of nearly all data that the server has. Every version of every file for the history of the project is pulled down when you run git clone.
You clone a repository with git clone [url] . For example, if you want to clone the Ruby Git library called Grit, you can do so like this:
$ git clone git://github.com/schacon/grit.git
That creates a directory named “grit”, initializes a .git directory inside it, pulls down all the data for that repository, and checks out a working copy of the latest version. If you go into the new grit directory, you’ll see the project files in there, ready to be worked on or used. If you want to clone the repository into a directory named
something other than grit, you can specify that as the next command-line option:
$ git clone git://github.com/schacon/grit.git mygrit
That command does the same thing as the previous one, but the target directory is called mygrit.
Git has a number of different transfer protocols you can use. The previous example uses the git:// protocol, but you may also see http(s):// or user@server:/path.git ,
which uses the SSH transfer protocol.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Examining local changes, comparing working copy to repository

A

Viewing Your Staged and Unstaged Changes
If the git status command is too vague for you — you want to know exactly what you changed, not just which files were changed — you can use the git diff command.

Let’s say you edit and stage the README file again and then edit the benchmarks.rb file without staging it. If you run your status command, you once again see something like this:
$ git status

To see what you’ve changed but not yet staged, type git diff with no other arguments:
$ git diff

That command compares what is in your working directory with what is in your staging area. The result tells you the changes you’ve made that you haven’t yet staged.
If you want to see what you’ve staged that will go into your next commit, you can use git diff -cached . (In Git versions 1.6.1 and later, you can also use git diff
-staged , which may be easier to remember.) This command compares your staged changes to your last commit:
$ git diff –cached

It’s important to note that git diff by itself doesn’t show all changes made since your last commit — only changes that are still unstaged. This can be confusing, because if you’ve staged all of your changes, git diff will give you no output.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Updating working copy, merging changes

A

Committing Your Changes
Now that your staging area is set up the way you want it, you can commit your changes. Remember that anything that is still unstaged — any files you have created or modified that you haven’t run git add on since you edited them — won’t go into this commit.
They will stay as modified files on your disk. In this case, the last time you ran git status, you saw that everything was staged, so you’re ready to commit your changes.
The simplest way to commit is to type git commit :
$ git commit

You can see that the default commit message contains the latest output of the git status command commented out and one empty line on top. You can remove these
comments and type your commit message, or you can leave them there to help you remember what you’re committing. (For an even more explicit reminder of what you’ve modified, you can pass the -v option to git commit. Doing so also puts the diff of your change in the editor so you can see exactly what you did.) When you exit the editor, Git creates your commit with that commit message (with the comments and diff stripped out).
Alternatively, you can type your commit message inline with the commit command by specifying it after a -m flag, like this:
$ git commit -m “Story 182: Fix benchmarks for speed”

Skipping the Staging Area
Although it can be amazingly useful for crafting commits exactly how you want them, the staging area is sometimes a bit more complex than you need in your workflow. If you want to skip the staging area, Git provides a simple shortcut. Providing the -a
option to the git commit command makes Git automatically stage every file that is already tracked before doing the commit, letting you skip the git add part

Basic Merging
Suppose you’ve decided that your issue #53 work is complete and ready to be merged into your master branch. In order to do that, you’ll merge in your iss53 branch, much like you merged in your hotfix branch earlier. All you have to do is check out the branch you wish to merge into and then run the git merge command:
$ git checkout master
$ git merge iss53

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Updating to specific version

A

Issues with Submodules
Using submodules isn’t without hiccups, however. First, you must be relatively careful when working in the submodule directory. When you run git submodule update, it checks out the specific version of the project, but not within a branch. This is called having a detached head — it means the HEAD file points directly to a commit, not to
a symbolic reference. The issue is that you generally don’t want to work in a detached head environment, because it’s easy to lose changes. If you do an initial submodule
update , commit in that submodule directory without creating a branch to work in, and then run git submodule update again from the superproject without committing in the meantime, Git will overwrite your changes without telling you. Technically you won’t lose the work, but you won’t have a branch pointing to it, so it will be somewhat
difficult to retrieive.
To avoid this issue, create a branch when you work in a submodule directory with git checkout -b work or something equivalent. When you do the submodule update a second time, it will still revert your work, but at least you have a pointer to get back to.
Switching branches with submodules in them can also be tricky. If you create a new branch, add a submodule there, and then switch back to a branch without that submodule, you still have the submodule directory as an untracked directory:
$ git checkout -b rack
Switched to a new branch “rack”
$ git submodule add git@github.com:schacon/rack.git rack
Initialized empty Git repository in /opt/myproj/rack/.git/

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Examining change history

A

Viewing the Commit History
After you have created several commits, or if you have cloned a repository with an existing commit history, you’ll probably want to look back to see what has happened.
The most basic and powerful tool to do this is the git log command.

By default, with no arguments, git log lists the commits made in that repository in reverse chronological order. That is, the most recent commits show up first. As you
can see, this command lists each commit with its SHA–1 checksum, the author’s name and e-mail, the date written, and the commit message.

A huge number and variety of options to the git log command are available to show you exactly what you’re looking for.
One of the more helpful options is -p , which shows the diff introduced in each commit. You can also use -2 , which limits the output to only the last two entries
This option displays the same information but with a diff directly following each entry. This is very helpful for code review or to quickly browse what happened during a series of commits that a collaborator has added. You can also use a series of summarizing options with git log . For example, if you want to see some abbreviated stats
for each commit, you can use the –stat option. As you can see, the –stat option prints below each commit entry a list of modified files, how many files were changed, and how many lines in those files were added and removed. It also puts a summary of the information at the end. Another really useful option is –pretty . This option changes the log output to formats other than the
default.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Discarding changes

A

Changing Your Last Commit
One of the common undos takes place when you commit too early and possibly forget to add some files, or you mess up your commit message. If you want to try that commit again, you can run commit with the –amend option:
$ git commit –amend
This command takes your staging area and uses it for the commit. If you’ve have made no changes since your last commit (for instance, you run this command it imme-
diately after your previous commit), then your snapshot will look exactly the same and all you’ll change is your commit message.
The same commit-message editor fires up, but it already contains the message of your previous commit. You can edit the message the same as always, but it overwrites
your previous commit.
As an example, if you commit and then realize you forgot to stage the changes in a file you wanted to add to this commit, you can do something like this:
$ git commit -m ’initial commit’
$ git add forgotten_file
$ git commit –amend
All three of these commands end up with a single commit — the second command replaces the results of the first.

Unstaging a Staged File
The next two sections demonstrate how to wrangle your staging area and working directory changes. The nice part is that the command you use to determine the state
of those two areas also reminds you how to undo changes to them. For example, let’s say you’ve changed two files and want to commit them as two separate changes, but you accidentally type git add * and stage them both. How can you unstage one of the two?
Right below the “Changes to be committed” text, it says use git reset HEAD … to unstage. So, let’s use that advice to unstage the benchmarks.rb file:
$ git reset HEAD benchmarks.rb

Unmodifying a Modified File
What if you realize that you don’t want to keep your changes to the benchmarks.rb file? How can you easily unmodify it — revert it back to what it looked like when you last committed (or initially cloned, or however you got it into your working directory)?
Luckily, git status tells you how to do that, too.
Let’s do what it says:
$ git checkout – benchmarks.rb

You can see that the changes have been reverted. You should also realize that this is a dangerous command: any changes you made to that file are gone — you just copied another file over it. Don’t ever use this command unless you absolutely know that you don’t want the file. If you just need to get it out of the way, we’ll go over stashing and branching in the next chapter; these are generally better ways to go. Remember, anything that is committed in Git can almost always be recovered. Even
commits that were on branches that were deleted or commits that were overwritten with an –amend commit can be recovered. However, anything you lose that was never committed is likely never to be seen again.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Resolving conflicts in code

A

Basic Merge Conflicts
Occasionally, this process doesn’t go smoothly. If you changed the same part of the same file differently in the two branches you’re merging together, Git won’t be able to
merge them cleanly. If your fix for issue #53 modified the same part of a file as the hotfix , you’ll get a merge conflict that looks something like this:
$ git merge iss53

Git hasn’t automatically created a new merge commit. It has paused the process while you resolve the conflict. If you want to see which files are unmerged at any point
after a merge conflict, you can run git status

Anything that has merge conflicts and hasn’t been resolved is listed as unmerged.
Git adds standard conflict-resolution markers to the files that have conflicts, so you can open them manually and resolve those conflicts. Your file contains a section that looks something like this:
«««< HEAD:index.html

<div>contact : email.support@github.com</div>

This means the version in HEAD (your master branch, because that was what you had checked out when you ran your merge command) is the top part of that block
(everything above the ======= ), while the version in your iss53 branch looks like everything in the bottom part. In order to resolve the conflict, you have to either choose
one side or the other or merge the contents yourself.
This resolution has a little of each section, and I’ve fully removed the «««< , ======= , and&raquo_space;»»> lines. After you’ve resolved each of these sections in each con-
flicted file, run git add on each file to mark it as resolved. Staging the file marks it as resolved in Git. If you want to use a graphical tool to resolve these issues, you can run git mergetool , which fires up an appropriate visual merge tool and walks you through the conflicts

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Copying, moving files, folders in repository

A

Moving Files
Unlike many other VCS systems, Git doesn’t explicitly track file movement. If you rename a file in Git, no metadata is stored in Git that tells it you renamed the file. However, Git is pretty smart about figuring that out after the fact — we’ll deal with detecting file movement a bit later.
Thus it’s a bit confusing that Git has a mv command. If you want to rename a file in Git, you can run something like
$ git mv file_from file_to
and it works fine. In fact, if you run something like this and look at the status, you’ll see that Git considers it a renamed file

Git figures out that it’s a rename implicitly, so it doesn’t matter if you rename a file that way or with the mv command. The only real difference is that mv is one command instead of three — it’s a convenience function. More important, you can use any tool you like to rename a file, and address the add/rm later, before you commit.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Committing changes

A

Committing Your Changes
Now that your staging area is set up the way you want it, you can commit your changes.
Remember that anything that is still unstaged — any files you have created or modified that you haven’t run git add on since you edited them — won’t go into this commit.

They will stay as modified files on your disk. In this case, the last time you ran git status , you saw that everything was staged, so you’re ready to commit your changes.
The simplest way to commit is to type git commit :
$ git commit

You can see that the default commit message contains the latest output of the git status command commented out and one empty line on top. You can remove these
comments and type your commit message, or you can leave them there to help you remember what you’re committing. (For an even more explicit reminder of what you’ve modified, you can pass the -v option to git commit . Doing so also puts the diff of your change in the editor so you can see exactly what you did.) When you exit the editor, Git creates your commit with that commit message (with the comments and diff stripped
out).
Alternatively, you can type your commit message inline with the commit command by specifying it after a -m flag, like this:
$ git commit -m “Story 182: Fix benchmarks for speed”

How well did you know this?
1
Not at all
2
3
4
5
Perfectly