Elvenware

Git

Welcome to Git

Git Basics

Git is a free, open source version control system. It allows you to track the different versions of code that you produce. You can roll time back, finding old versions of your code, and you can compare one version with another version.

Version control has been around a long time. What makes Git different is the way it handles repositories. Git has a distributed model that allows you to clone repositories onto your local machine. In the past, there was only version of the repository, typically somewhere on your local internet. Git is quite different, in that you can have multiple versions of your repository. This means you can have a local version of the repository and check in and check out from there before deciding that you want to push to the
main repository.

Git is a sophisticated tool, and it is not easy to learn. It is however, the dominant tool of its kind, and with good reason. It is powerful, flexible, and robust. Ultimately, Git is worth mastering. In fact, much of the basic functionality of Git is relatively easy to master once you understand how it works.

Centers of Activity

Below you find links to the main site for Git. You will also find their download page and related links:

There are many Git servers in the cloud. Here are two that everyone should know about:

You probably want to have accounts on both GitHub and BitBucket. Everyone uses GitHub, and bitbucket competes by allowing you to have free private accounts.

GIT Guides found on the Web

I've put together various resources include a video. I've also put together a walk through/assignment that can help you get started with Git on Linux. Also, I have a slide deck on git.

There is an excellent free online book:

More useful links:

Is Git Reliable?

Topics like "is git reliable?", "is it me, or is it git?" come up from time to time. My concern is that students will spin their wheels, focusing on imaginary problems with git, rather than trying to problem solve.

The following stats were assembled in Feb, 2016:

Related links which probably contain updates to total github users:

Of course, it is always possible that one of us will hit a bug in git. All I can say is that the odds we hit a git bug when performing basic operations with git is very, very low.

Git is not perfect. No piece of software is perfect. But git is, in my opinion, one of the most reliable, tested, and proven software programs in the world.

Git vs DropBox

There are some tools such as DropBox, GoogleDrive and OneDrive that will automatically propagate changes to any machine that subscribes to updates. Git, on the other hand, is designed to let developers, or authors, work on a set of documents and then share them when and as needed. The advantages to the second scenario (git), for a certain type of activity, are two fold:

I think all that goes at least some of the way to explaining why Git works the way it does.

Sharing documents is a good and common use of a tool like Git. When working with documents rather than code, the consequences of automatically pushing updates is less severe, but still real. Suppose I'm putting together a list of steps that must be followed. If I accidentally leave out a few steps in my first draft, then people who are following my changes live are likely to be miffed that my guide does not work. Or perhaps we begin working on a description of a seven step process. We get to step 5, then get called to dinner, or head off to bed. People reading the document would then begin following my steps, only to find that the description is incomplete.

There are, of course, strong arguments for exactly the opposite kind of functionality. If I'm taking notes on a lecture in Evernote, at the end of class I want them to be propagated automatically and immediately so I can access them as needed from home. There is no advantage in that scenario to any other system. Leaving a copy of my notes at school without being able to access them from home would have no advantages. I want to see all changes immediately on all machines where I log into Evernote.

The bottom line is that there are many different ways to share information in the cloud. The only way to learn which one is best for which task is to learn the tools, and begin experimenting with them. Everyone is working these things out for themselves just now. Unfortunately, students in the future will probably be told to use tool X for purpose Y, and tool W for Z. Neat and clean, but not nearly as fun as exploring this world on your own. We live in interesting times.

Install Git on Windows

There are two applications you might want to install. The primary GIT application is found here:

When you run the install shown above, be sure to choose the option that gets GIT on your path. Also, you will probably want to set GIT up to work with PLink (Putty), and you will want to choose an option that ensures that you have LF style line endings on files that you plan to use on a Linux box.

Because GitHub is so ubiquitous, you will probably want to install it's GUI front end also:

You can use Git-Scm with Git-Hub, but there is no denying that the GitHub GUI makes dealing with repositories on GitHub very easy.

Clone on Desktop

On GitHub, you might notice a button that says Clone on Desktop. If set up correctly, this button will allow you to clone a repository using a GUI application. On Windows, this application is called GitHub for Windows. For a variety of reasons, I think it is ultimately best to manage your Git repositories from the command line, but there is no denying that this GUI interface can be useful, particularly if you only plan to use GIT from Windows.

In theory, you should be able to just push the Clone on Desktop button and the process of installing GitHub for Windows should occur automatically. In practice, this does not always happen. Unfortunately, there are variety of reasons why this call might fail, depending on the browser and system you are using. My suggestion is to simply install GitHub for Windows manually, and then the button should begin working as you expect.

To install GitHub for Windows go to the first link below and push the big green button:

A file called GitHubSetup.exe should soon be downloaded to your downloads folder. You can run it from there. The install is a forehead install, and should be navigable by everyone in your family from your cat on up. I believe that it does not require admin privileges to run this install.

While you are at it, and if you have not already done so, you should also install git:

Configuring GIT

I describe how to use GitGui below. If you are working from the command line, go to your home directory and open .gitconfig. You may prefer to just view this file, and use the commands mentioned below to edit it. Or, you could edit it directly. The changes that need to be made are fairly obvious.

My .gitconfig looks something like this:

[user]
email = someone@somewhere.com
name = Charlie CedarIsle Calvert
[push]
default = simple
[gui]
recentrepo = C:/Src/Git/Simple02
recentrepo = C:/Src/Git/Writing
recentrepo = C:/Src/Git/CloudNotes
recentrepo = C:/Src/Git/JsObjects

As you can see, I've set up the email, name and push | default options. If you don't set up your user name and email as described above, you may see this message at some point:

Your name and email address were configured automatically based
on your username and hostname. Please check that they are accurate.
You can suppress this message by setting them explicitly:

    git config --global user.name "Your Name"
    git config --global user.email you@example.com

After doing this, you may fix the identity used for this commit with:

    git commit --amend --reset-author

To resolve this message, just follow the instructions. For instance, you could type something like the following:

    git config --global user.name "Charlie Calvert on EC2"
    git config --global user.email charlie@example.com

When you do this, you are changing the settings in your ~/.gitconfig file.

If you type the last suggested command, the one about amending your commit, you may end up in an editor such as nano or vim. If you can handle that, then go ahead and do it, otherwise, you can relatively safely skip that step. All that will be missing is the name of the committer in your repository for this particular commit. Later commits will have your name.

You may also see a message like this:

warning: push.default is unset; its implicit value is changing in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the current behavior after the default changes, use:

  git config --global push.default matching

To squelch this message and adopt the new behavior now, use:

  git config --global push.default simple

I respond to this message by typing the following:

git config --global push.default simple

For those who are interested, let's leave the command line and discuss GitGui. When you first open GitGui, you should set up your user name and email. From the menu, select Edit | Options.

What you write in the GitGui options ends up in the email and name fields of .gitconfig, and vice-versa. It is important to have both of these fields filled out from the start.

If you change the information for a particular repository in GitGui so that it differs from your global values, then that information will appear in the config file from your repository. If it is the same as the global values, the same as the values .gitconfig, then it will not appear in the config file for your repository.

There is a third option beyond what is mentioned above. You can look at the .git/config file from a particular repository. Remember that .git directory is hidden on both Windows and Linux. I usually only open config file if I want to change the URL for a repository, but you can do more in there if you want. For now, I'll leave those options up to you to explore.

Repository Name

You can see the URL and name for your repository with any of the following commands:

cat .git/config   // From the root of your repo
git config --get remote.origin.url
git remote show origin

For instance, here is git config:

$ git config --get remote.origin.url
git@github.com:charliecalvert/JsObjects.git

And here is show remote origin:

$ git remote show origin
* remote origin
  Fetch URL: git@github.com:charliecalvert/JsObjects.git
  Push  URL: git@github.com:charliecalvert/JsObjects.git
  HEAD branch: master
  Remote branches:
    MakeHtmlConvert tracked
    master          tracked
  Local branches configured for 'git pull':
    MakeHtmlConvert merges with remote MakeHtmlConvert
    master          merges with remote master
  Local refs configured for 'git push':
    MakeHtmlConvert pushes to MakeHtmlConvert (up to date)
    master          pushes to master          (up to date)

Running GIT

If you use Putty, it will probably be simplest for you if you use GIT with plink, which is part of Putty. During the GIT SCM install, you have the chance to set this up, as shown in the screenshot below:

Use plink

![Use plink](http://elvenware.com/charlie/development/cloud/images/GitScm01.png)

If you have already installed GIT, don't despair! All the option above does is set an environment variable named GIT_SSH. If you chose the option shown in the screenshot, then when you type SET at the command line, you should find something like the following value for GIT_SSH:

GIT_SSH=C:\Program Files (x86)\PuTTY\plink.exe

Since you have a deep knowledge of envirnment variables, you know how to modify this variable using the Environment Variables dialog.

Set GIT_SSH env variable

![Set GIT_SSH env variable](http://elvenware.com/charlie/development/cloud/images/GitScm02.png)

Here is an example of how to configure the variable:

Set the GIT_SSH environment variable

![Set the GIT_SSH environment variable](http://elvenware.com/charlie/development/cloud/images/Git07.png)

If you have been living a sheltered life, you can learn about about environment variables here:

A related issue is the "Host Key is not Cached" error, which is discussed elsewhere in this document.

Install Git on Ubuntu Server

It may be helpful to setup a default editor. You can do so by running either of the following at a nix command prompt:

export EDITOR=nano
export EDITOR=vim

Now install git:

sudo apt-get install git

Create a directory named git and navigate to it:

mkdir git
cd git

Now pull down a repository that you do not own, and hence will be in read only mode:

git clone git://github.com/charliecalvert/JsObjects.git

You can go to GitHub and create a repository and put a readme file in it. If you own the repository, or have rights to it, then the command might look more like this:

git clone https://github.com/charliecalvert/bc-basic.git

Git GUI Basics

First line for HTTPs clone:

Second line, say where you want it on your system:

C:\\Users\\Me\\Documents\\Git\\JsObjects

Clone Repository

![Clone Repository](http://www.elvenware.com/charlie/development/cloud/images/GitGui04.png)

Do a pull with GitGui:

The following images take you step by step through the process of committing changes to a repository.

Unstaged Changes

![Unstaged Changes](http://www.elvenware.com/charlie/development/cloud/images/GitGui07.png)

Ready to Commit

![Ready to Commit](http://www.elvenware.com/charlie/development/cloud/images/GitGui08.png)

Get ready to Push

![Get ready to Push ](http://www.elvenware.com/charlie/development/cloud/images/GitGui05.png)

Push Success

![Push Success](http://www.elvenware.com/charlie/development/cloud/images/GitGui06.png)

You can choose Repository | Visualize XXX History to get an overview of what has been happening in the repository:

Visualize History

![Visualize History](images/GitHistory01.png)

Git Help Command

To learn about a git command, type git help . For instance:

git help status
git help commit
git help add

In some cases the results will be displayed at the command line. In others, you will be taken to a web page.

The Git Status Command

Suppose you have a file called users.html that you have edited. Here is how to confirm that you have edited the file, and to see if there are other files you may have edited, added, or deleted from or to the repository:

git status

I issue the git status command frequently. It lets me know the state of my repository. Here for instance, is what you will see if you issue the git status command after first creating users.html:

C:\Git\foo> echo foo > users.html
G:\Src\Git\foo> git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       users.html
nothing added to commit but untracked files present (use "git add" to track)

When we typed git status, git replied by telling us the status of the files in your directory. In particular, git is telling us that we have a new file in our repository called users.html. When git sees a new file or directory in a repository, then the status message is usually "Untracked files." This is git's way of saying "I see this new file or directory in your repository, and I'm not currently tracking it." Then it adds a hint: "(use "git add ..." to include in what will be committed)." It is telling you that you can use the command git add users.html to start tracking the folder that is currently not being tracked. There are several variants on the git add command that you can run. For instance, typing git add . (that's git plus add plus a period) will usually add any untracked or modified files to the repository without you having to spell them out.

Git Log

You can just type git log, but I find git log --pretty-oneline more useful. For instance:

git log --pretty=oneline
7ff2d4b4d3dcea2d938a2a7e0ed42e1000cb32a2 Readme has Elvenware patterns document
e16107b10dce32d7abba9f973549222e5db7adf0 Clean up names for code readability
8739b8e3deee016ad5025f6764ef36ab571ff71c Add functional Programming examples

Now clone a particular commit:

git branch functional 8739b8e3d

Now you can switch to the functional branch and see your files at the time of that commit.

git checkout functional

If you are tired of looking at that branch, you can switch back to the master branch and then permanently remove your functional branch:

git checkout master
git branch --delete functional

Show History of a File

First type git log to see the times when you modified a file. For instance, this shows the history of .gitignore in a repository:

$ git log --oneline .gitignore
83925f1 updated .gitignore from a samed-name file
08e7f2b Adding Object Basics and SplitSlice
bd54f9f adding gitignore

Now show the contents of a particular version of the file with git show:

$ git show 08e7f2:.gitignore
node_modules
.metadata
.idea
Thumbs.db
.c9

What we have done is type:

Another example, this time using the README file:

$ git log --oneline README.md
01f3de2 Bar
2b593a3 asdf
f182b3c readme changed
ce4e0f2 README update
66f33ac initial Commit

Then view a particular version:

$ git show f182b3c:README.md
My Readme File
This is an update
New change after support for ssh

Working with Files in a Repository

Suppose you have a file called users.html that you have either recently created or recently edited. You now need to tell git to check in your changes, that is, to add your new or updated file to the repository. Whether the file is new or updated, here is how to check in the users.html file:

git add users.html
git commit -m "This is my message about the way I updated users.html"

Now you have everything checked into your local copy of the Git repository. To push these changes to your GitHub, BitBucket or other remote copy of the repository, issue this command:

git push

Now, lets add a folder named temp and a file named foo.txt to the repository:

mkdir temp  
cd temp
echo foo > foo.txt
cd ..

The above commands create a new folder called temp. The second line shows how to navigate into the folder. The third line creates a new file called foo.txt with the contents being the single world foo. Finally we navigate back to the original directory where we were before we created the temp directory.

Now add the changes and commit the work to the repository:

git add temp  
git commit -m "adding temp"  

After issuing the last command, you will see several lines of feedback from git. Now it is time to push your changes up to GitHub (or to wherever your repository is stored)L

git push  

If you do not have an SSH key and repository set up, you will now be asked for your username and password.

Sometimes you will have conflicts between what is on the server and what you have on your machine. In that case, do a merge. This will create a local copy of you files with both sets of text in it. Open your file in a text editor, find the places where the conflicts exist, fix them manually, and then go through another add/commit/push cycle.

A Typical Git Session

Suppose you run git status and get this result:

>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   Cordova/CordovaInput/assets/www/index.html
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       foobar.txt

Here you can see that one file, index.html, has been modified. Another file, called foobar.txt, has been created. To commit them both, do this:

git add .

That's git add followed by a period. The period is a wild card saying: "Add everything that has been changed." Now check your status:

>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   Cordova/CordovaInput/assets/www/index.html
#       new file:   foobar.txt
#

As you can see, there are now two files witing to be committed. You can commit them like this:

>git commit -m "Update index.html and add foobar.txt to to ward off polen smears."
[master c91d5fc] Update index.html to support tiger attacks, add foobar.txt to ward off polen smears.
 2 files changed, 2 insertions(+), 1 deletion(-)
 create mode 100644 foobar.txt

Now push them to the repository:

>git push
Counting objects: 14, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (8/8), 633 bytes | 0 bytes/s, done.
Total 8 (delta 5), reused 0 (delta 0)
To git@github.com:charliecalvert/Simple02.git
   16e7d40..c91d5fc  master -> master

If you type git reset then you can undo an add. When you issue a git add command you move one or more modified files to the staging area. If you type git reset you move them back out of the staging area. You undo the add.

The GitIgnore File

You can create a file .gitignore that tells git to ignore certain files. That's GITIGNORE, but in all lowercase, with a PERIOD in front of it: .gitignore. Remember that in Linux files that have a period in front of them are hidden. At the command prompt, you can view them by typing ls -la.

You might create a file called .gitignore with this content:

Thumbs.db

This tells Git to ignore all the files in your repository that are called Thumbs.db.

You could then tell it to ignore the contents of the temp directory, the .metadata directory and the node_modules directory:

Thumbs.db
temp/*
.metadata
node_modules

Here is how to ignore everything but the source directory:

*
!Source/

And here is a complex example that includes a comment:

# Ignore everything in the "charlie" directory, except certain directories

charlie/*
charlie/development/*
charlie/development/web/*
charlie/development/web/JavaScript/images/
charlie/development/web/HtmlGuide/images/
charlie/development/web/CssGuide/images/
!charlie/development/
!charlie/development/web/
!charlie/development/web/CssGuide/
!charlie/development/web/JavaScript/
!charlie/development/web/HtmlGuide/

Add and check in your .gitignore file:

add .gitignore
commit -m "Adding GitIgnore File"
git push

Create a New Repository

Suppose you want to create a new repository. Typically you would start by creating the repository on a remote service such a BitBucket or Github. After signing into your BitBucket or GitHub account, you will see a button for creating the repository. Push the button and create an empty repository. Don't put anything in the repository, not even a README.md file. Just leave it empty.

Next, make sure you have an SSH private key and public key pair on your client machine, and store the public key portion in BitBucket or GitHub, depending on the service you are using. To do this, first open the public key (id_rsa.pub) in an editor such as geany, nano or notepad++. Block copy it, then paste it into the appropriate field on the GitHub or BitBucket web site. In particular, on BitBucket, choose manage account from the icon in the upper right portion of the BitBucket site, and then select ssh keys from the menu on the left. On GitHub, choose the gear icon at the top right of the site, and then select SSH keys from the menu on the left. Don't forget to call ssh-add on your private (id_rsa) key, as described at the link shown above.

Now that you have SSH set up, go to the directory you want to use as the basis for your repository. If you don't have such a repository, then start from scratch:

mkdir MyRepo
cd MyRepo

No initialize git by typing typing git init. This converts an ordinary directory into a Git repository. In particular, it creates a hidden directory called .git. In that directory, it stores all the files necessary to configure and maintain your repository.

Then get the command from bitbucket or github that is used to designate where your remote repository resides. The command might look like this, though the exact url at the end will likely differ in your case:

git remote add origin ssh://git@bitbucket.org/lucia/myrepo.git

Then add some content to your repository and add and commit your content:

echo MyReadMe > README.md
git add README.md
git commit -m "Adding a readme to my new repo"

If you have multiple changes that you want to add and commit at one time, then use a period in your call to git add:

git add .
git commit -m "Initial commit"

In the first example, we added just a README.md file to the repository. In the second example, we added any new or updated files to your repository.

Finally, add the following command to push your repo to BitBucket:

git push -u origin --all

After this first time, you will be able to push like this:

git push

It's only the first time you need to add -u origin --all.

To sum up, if you have an existing repository, and you want to push it from the shell do something like this, where the exact URL for your repository will of course be different from the one shown here:

git remote add origin git@github.com:charliecalvert/DeleteMeNow.git
// Add some content
git push -u origin master

Sometimes you will have a session that looks like this:

G:\Src\Git\foo>git push -u origin --all
No refs in common and none specified; doing nothing.
Perhaps you should specify a branch such as 'master'.
fatal: The remote end hung up unexpectedly
Everything up-to-date

If you get an error like that, it means you forgot to run git add and git commit.

Here is a summary of the steps:

Here are the instructions from GitHub, which are just a variation on what I write above:

echo "Some Text" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:charliecalvert/DeleteMeNow.git
git push -u origin master

Here are the instructions from BitBucket, which are just just a variation on what I say above:

mkdir /path/to/your/project
cd /path/to/your/project
git init
git remote add origin git@bitbucket.org:ccalvert/deletemenow3.git

echo "Charlie Calvert" >> contributors.txt
git add contributors.txt
git commit -m 'Initial commit with contributors'
git push -u origin master

More on git remote add origin

Sometimes the issue can also be that you need to set to the URL with command like this:

git remote add origin ssh://git@bitbucket.org/lucia/myrepo.git

Suppose you create a new repository:

mkdir foo
$ cd foo
$ git init

Now you have a new repository. Your .git/config file looks like this:

$ cat .git/config
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
charlie@MountainStreamsLinux

Now run our command and then look at .git/config

$ git remote add origin ssh://git@bitbucket.org/lucia/myrepo.git
~/temp/foo
$ cat .git/config
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[remote "origin"]
    url = ssh://git@bitbucket.org/lucia/myrepo.git
    fetch = +refs/heads/*:refs/remotes/origin/*
~/temp/foo

Git CONFIG File

The git config file holds several important facts. One of the most important is the URL used to access to your git repository.

The config file is called config and it is stored in a hidden directory at the root of your repository called .git.

To set your global user.email, you can write something like this:

git config --global user.email "me@somewhere.com"

This information is usually written to $HOME/.gitconfig. Here is a sample file:

$ cat ~/.gitconfig
[user]
    email = charlie@example.com
    name = Charlie on RohanElf
[push]
    default = simple
[diff]
    tool = meld

On Windows, you might find the file here:

c:\users\username\.gitconfig

On Linux:

\home\username\.gitconfig

Or on the Mac:

\users\username\.gitconfig

If you are guru, and this gets really mixed up, try looking in:

C:\Program Files (x86)\Git\etc\profile

In particular, one time I found my environment had a variable in it called HOME that was pointing to some nonsense value like /home/ubuntu, even though I was on a Windows machine.

In the .get directory you will find your config file. You can just edit that file with a text editor. On the other hand, you can use the following to set values in the global config file found in the main .git directory. The main .git directory for a user is usually found in your home directory on both Windows in Linux.

C:\\Users\\Charlie\>git config --global user.name "charliecalvert"
C:\\Users\\Charlie\>git config --global user.email "noone@nowhere.net"

You can pass in a length of time you want git to cache your password:

git config --global credential.helper cache --timeout=900

Git Overwrite Local Changes

If you want to refresh the entire local repository, overwriting your changes, do this:

git fetch
git reset --hard

Or, you can specify the repository you want to reset from:

git fetch
git reset --hard origin/master

If you have a single file in the local repository that you have edited, and you want to throw away those changes, but keep your other changes, then do this:

git fetch
git checkout origin/master <MyFile>

Git URLs and SSH

There are two primary types of Git URLs:

I prefer using SSH since it does not involve using a password. In my opinion passwords are both more difficult to use and more likely to cause a security risk than an SSH key. Also, it is useful to understand how SSH keys work. It is, in my opinion, useful to know how to:

Here is how to think about the parts of a git URL:

git@github.com:<MY_USERNAME>/<MY_REPOSITORY>.git
https://github.com/<MY_USERNAME>/<MY_REPOSITORY>.git

There are three ways to interact with a GIT repository on a site like GitHub or BitBucket:

NOTE: Many tools, such as GitHub for Windows, can handle your password for you automatically. That means they work well with HTTPS connections. To get that benefit, you need to be in the GitHub GUI, or in the shells associated with those GUI's. These tools work well, and I encourage you to use them. However, there are times when it is nice to be able to check in and out of Git without having to use a specific tool. In those cases, many people will find it easiest to use SSH.

To connect to a site using SSH, you need to do two things:

As mentioned above, there are several types of URLs you can use with Git. Only one of them sets up your repository to use SSH. When you use a URL that looks like the one shown below, then you are saying you want to work with a read/write copy of the repository in SSH mode:

git@github.com:someone/JsObjects.git

None of this is specific to GitHub. The same syntax applies when working with files stored in any remote repository. Here, for instance, is an SSH URL for bitbucket:

git@bitbucket.org:someone/deleteme.git

Here is a URL for an HTTPS readwrite connection to a repository:

https://github.com/charliecalvert/bc-basic.git

These URLs are easy to identify since they begin with the letters HTTPS.

NOTE: Further down in this document you will learn about creating URLs for local repositories.

Finding and Changing Git URLs

Recall that the URL for your repository is usually kept in the .git directory at the root of your repository. (This folder is usually hidden, even on Windows. You can look for hidden files or folders, or just go ahead and type cd .git, and assume it will work if you are in the root of your repository.) If you want to change the URL your repository uses by default, you can open the config file in your .git directory and edit this line:

[remote "origin"]
url = git@github.com:charliecalvert/JsObjects.git

You can view it by typing: git remote show origin. Alternatively, try this:

git config --get remote.origin.url

Here is the line in the config file that specify's your URL:

[remote "origin"]
    url = git@github.com:charliecalvert/JsObjects.git
    fetch = +refs/heads/*:refs/remotes/origin/*

The conventional way to see this information is with the following command:

git remote show origin

At runtime, it might look like this:

$ git remote show origin
* remote origin
  Fetch URL: git@github.com:charliecalvert/JsObjects.git
  Push  URL: git@github.com:charliecalvert/JsObjects.git
  HEAD branch: master
  Remote branches:
    MakeHtmlConvert tracked
    master          tracked

Alternatively, try one of these:

git config --get remote.origin.url
git remote -v

To set the URL, do this:

git remote set-url origin git@github.com:my-user-name/my-repo.git

If you are uncertain about the URL for your repository, you should able to easily find it on either GitHub or BitBucket.

The SSH Key

If you are going to be using SSH to connect to your repository, then you want to be sure that you have your key loaded into memory. On Windows, I use a Pageant (PuTTY). When you clone, it helps a lot if you use Pagaent. On Linux, you can use ssh-agent and ssh-add.

NOTE: When working on Windows, be sure to check your /users/username/.ssh folder for keys that tools like GitHub might be using. This will only come into play if you are not using plink to handle your keys.

Caching SSH Keys on Linux

On Linux, this is how I load an SSH key into memory so that I don't have to reference it each time I make a connection. (On Windows we use Pageant to perform this task.)

First I need to be sure ssh-agent is load. Then I add the key to the store. Notice that in the first command we use backticks:

eval%20%60ssh-agent%60%0Assh-add%20MyKey.pem

Try putting this in .bashrc to load ssh-agent when you log in to your bash shell:

if [ -z "$SSH_AUTH_SOCK" ] ; then
  eval `ssh-agent`  
fi

If you put in .bashrc then it will get loaded each time you open the shell, so you can end up with multiple copies of ssh-agent running. But you need to have it running in the current shell, so I don't see a good way around this.

You can also create ~/.ssh/config file that associates a domain with key. Here is the complete contents of an example file for connecting GitHub.com with a key called Github.pem:

Host github.com IdentityFile ~/.ssh/Github.pem

If you get a warning about an unprotected key file, that usually means that you have not set up the permissions correctly for the key file itself. You should ensure that only the current user can read it:

chmod 600 github_rsa

Or perhaps:

chmod 600 MyKey.pem

Now try again, and it should go smoothly.

ssh-add github_rsa

NOTE: I suppose you could even make the SSH keys readonly:

chmod 400 github_rsa

After doing this, you should be able to access sites where you have shared your ssh public key.

Git SSH Checklist

If you are having problems, check the following:

Useful video:

Host Key is not Cached

The following error is a classic:

"The servers host key is not cached in the registry...connection abandoned."

To fix this, Open Putty, and create a connection to BitBucket (bitbucket.org). Try to connect. You don't need to actually connect, you just need the dialog to pop up that asks if you want to store the key for the site locally. Answer yes. Then, if you actually connected, which is unlikely and unnecessary, disconnect. Now go back and try to connect again using SSH. It should now work.

Another option is to type the following at the command line:

plink MyUserName@bitbucket.org

This will also give you an option to store the key for the bitbucket server in your known_hosts file. The known_hosts file is usually stored in your ssh directory:

Amend a Commit

(I'm still working on this section of the text, and I have not fully tested the commands shown here.)

You can amend the previous commit (i.e. after removing a bad file or even simply to change the commit message... add -m "message" after --amend)

git commit --amend     
git push -f

If you ommit the push command you will an error message like this:

"...the tip of your current branch is behind..."

References:

<http://www.atlassian.com/git/tutorial/rewriting-git-history#!commit-amend>
<http://stackoverflow.com/questions/179123/how-do-i-edit-an-incorrect-commit-message-in-git>

This command can be useful if you accidentally sent some confidential files along with your original commit. That confidential info would still be tracked in history even though you removed it in subsequent updates.

For those who are new to Git, or new to Version Control, just a couple reminders:

Here is a notice from the Git documentation about this command:

"Don't Amend Public Commits

"On the git reset page, we talked about how you should never reset commits that have been shared with other developers. The same goes for amending: never amend commits that have been pushed to a public repository.

"Amended commits are actually entirely new commits, and the previous commit is removed from the project history. This has the same consequences as resetting a public snapshot. If you amend a commit that other developers have based their work on, it will look like the basis of their work vanished from the project history. This is a confusing situation for developers to be in and it's complicated to recover from."

If your previous commit is just completely bad, you can do a git reset --hard HEAD~1 (or ~2, 3 etc however far back you want to reset) to completely remove the last commit(s) and then a git push -f or --force but this removes everything that was changed in the last commit(s).

The git commit -amend affects only the files that were changed /removed since the last commit.

There is also git reset --soft that is similar to git commit --amend so one or the other may be preferred in a particular situation. But amending a commit seems to be a way to fix simple errors in a previous commit.

Clone a Local Repository

git clone file:////home/charlie/git/myrepo git clone file:////\$HOME/git/myrepo

GIT Remote

Suppose you have an existing repository on your local machine, and an empty repository on GitHub. Navigate to the folder that holds your local copy of the repository. If issued from that folder, the followling line ought to add your local repository to the empty GitHub repository:

git remote add origin ssh://git@bitbucket.org/ccalvert/deleteme.git

You can then type the following to see where your repository is set up:

git remote -v

Delete a remote repoistory called working:

git push origin origin :working

To delete a local branch named working:

git branch -d working

Working with Branches

Branches allow you to work with variations of your code. You can have two versions of one code base checked into your repository at the same time. Perhaps one version is intended for one audience, the other for another audience. Perhaps one version has code from Person A in it and the other branch has a second version of the same code, but with suggestions made by Person B.

List available branches:

git branch

For instance:

C:\GitHub\Simple02>git branch
* master
test

The listing above shows two branches, one called master, the other called test. The asterisk before the master branch means that it is the current branch.

Create a branch named myBranch:

git branch myBranch

For example:

C:\GitHub\Simple02>git branch myBranch

C:\GitHub\Simple02>git branch
* master
  myBranch
  test

As you can see, there is now a new branch called myBranch.

Now we can switch to our new branch:

git checkout myBranch

Now type git branch again and you will see both branches listed, but your current branch will be highlighted. In other words, myBranch will appear in highlighted text and with a star before it:

C:\GitHub\Simple02>git checkout myBranch
Switched to branch 'myBranch'

C:\GitHub\Simple02>git branch
  master
* myBranch
  test

Now lets show that you can edit and add files to your current branch without effecting the other branch. First edit a file and check your changes in to your branch:

git add MyFile
git commit -m "Committing changes to MyFile"

If you have cloned a repository with multiple branches, you can see those branches one of three or four ways:

Git branch -a shows all the branches, both local and remote. Git --list shows the local branches and git -r shows the remote branches.

C:\GitHub\Simple02>git branch -a
  master
* myBranch
  test

  remotes/origin/HEAD -> origin/master
  remotes/origin/master
  remotes/origin/test

In the listing shown above, you can see that master and test are on both the local and remote versions of the repository. But myBranch is only on the local version. In the next section you will learn how to push it to the remote repository.

If you want to push this branch, and only this branch, to the origin (to the remote repository), then do this:

$ git push --set-upstream origin BridgeReader04

Where BridgeReader04 is the name of the current repository.

Branch Differences

Show on the names of files that differ between two branches:

git diff --name-only branch01 branch02

For instance:

$ git diff --name-only week06-charlie week06
Week05-SolarExplorer/karma.conf.js
Week05-SolarExplorer/public/javascripts/app.js
Week05-SolarExplorer/spec/fixtures/renewable.html
Week05-SolarExplorer/views/main.jade
Week05-SolarExplorer/views/renewable.jade

Show the differences found in a specific file between two branches:

git diff week06 week06-charlie -- Week05-SolarExplorer/views/renewable.jade

For instance, suppose you were comparing the spec/test-basic.js file between the week07 and week07-charlie branch:

$ git diff week07 week07-charlie -- Week07-SolarExplorer/spec/test-basic.js
diff --git a/Week07-SolarExplorer/spec/test-basic.js b/Week07-SolarExplorer/spec/test-basic.js
index 929db3c..3207834 100644
--- a/Week07-SolarExplorer/spec/test-basic.js
+++ b/Week07-SolarExplorer/spec/test-basic.js
@@ -20,14 +20,14 @@ describe('Elvenware Simple Plain Suite', function() {
         scope = _$rootScope_.$new();
         $compile = _$compile_;
         $templateCache = _$templateCache_;
-        //        mainController = _$controller_('MainController', {
-        //            $scope: scope
-        //        });
+                _$controller_('MainController', {
+                    $scope: scope
+                });
     }));

     beforeEach(function() {
         jasmine.getFixtures().fixturesPath = 'base/spec/fixtures/';
-        loadFixtures('marie.html');
+        loadFixtures('marie.html', 'renewable.html');
     });

In the above code, the week07 branch has the part with the minus before it:

-        //        mainController = _$controller_('MainController', {
-        //            $scope: scope
-        //        });

My week07-charlie branch has the code shown below with a plus before it:

+                _$controller_('MainController', {
+                    $scope: scope
+                });

Let's assume for a moment, that week07 has broken code, and week07-charlie has fixes for the broken code. In that case, we can conclude that week07 was wrong to have commented out the controller code, and wrong to have tried to use mainController as a variable in this location. We can see this by comparing the code with the minus before it to the code with the plus before it.

Then a bit further down, the week07 branch has this code which is preceded by a minus sign:

-        loadFixtures('marie.html');

The code from week07-charlie looks like this:

+        loadFixtures('marie.html', 'renewable.html');

Assuming that week07-charlie has good code in it, then the fix to the code in week07 is shown with a plus sign.

Suppose you have two different branches where you have renamed a directory. How do you compare the same file between two different branches if the directory where the file has been renamed? State the branch, then a colon, then the path to the file. Like this:

git diff week06:Week05-SolarExplorer/spec/test-mocks.js week07:Week07-SolarExplorer/spec/test-mocks.js

You might also use git diff to compare two different versions of a single file found in two different commits. This asks what did file X look like in commit Y and in commit Z? You issue the command like this:

git diff e78fe3 a7e46a README.md

This compares the difference for a specific file between two commits. If you want to see the changes between all files in the two commits, just leave off the file name in the above command.

Update File from Branch

First checkout the branch where you want to update a file:

git checkout week10

Then patch a specific file from another branch:

git checkout --patch week09-charlie views/about.jade

This applies the difference from week09-charlie:views/about to week10. It "merges" the contents of the file in week10 with the contents of the file in week09-charlie.

Suppose week09-charlie:views/about.jade looked like this:

h1 FooBar

p qux.

Suppose week10:views/about.jade looked like this:

h1 FooBar

After the command was issued, week10:views/about.jade would look like this:

h1 FooBar

p qux.

We call this patching rather than merging because the changes are not automatically added and committed. It is as if you made the changes your self with the editor, and you must manually add and commit if you want to keep them.

Comparing Two Branches

If you want to compare the files from BranchA and BranchB, you might do something like this:

git diff --name-status branchA..branchB

For instance:

git diff --name-status master..charlie01

Here are the various kinds of status letters you see for the output of the above:

Push a Branches

The commands git push --all and git pull -all should pull and push everything on all branches.

Here is a push with --all when I had changes on two branches:

$ git push --all
Counting objects: 8, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (8/8), 754 bytes | 0 bytes/s, done.
Total 8 (delta 4), reused 0 (delta 0)
To git@bitbucket.org:ccalvert/prog219-calvert-2016.git
   9586409..1d7c4b2  week06 -> week06
   0f7f54a..b7fc171  week05 -> week05

Here is a push with git push when I had changes on both branches

$ git push
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 385 bytes | 0 bytes/s, done.
Total 4 (delta 2), reused 0 (delta 0)
remote:
remote: Create pull request for week06:
remote:   https://bitbucket.org/ccalvert/prog219-calvert-2016/pull-requests/new?source=week06&t=1
remote:
To git@bitbucket.org:ccalvert/prog219-calvert-2016.git
   d98ac56..9586409  week06 -> week06

Notice that in first case both week05 and week06 got pushed, while in the second only week06 got pushed.

To be sure, check on BitBucket/GitHub, or on separate machine, or just on a separate copy of your repository on the same machine.

https://git-scm.com/docs/git-push

Merge Branches

If you have been doing work in a branch called charlie and you want to merge it with master, then first checkout the master branch branch, then merge them:

git checkout master
git merge charlie

When performing a merge between branches, use the Git difftool:

git difftool -d --tool=meld master <SomeBranch>

Or, if you don't want to pass the --tool parmater each time, set it globally:

git config --global diff.tool meld

Then you get git automatically when you do something like this:

git difftool -d master <SOME BRANCH>

For instance,

git difftool -d master charlie

Push Local Branch to Remote

If you want to create a copy of your branch in the original remote repository:

git push <remote> <local>

For example:

git push origin myBranch

This would, for example, push the local branch called myBranch to the remote repository, which might be a place like GitHub or BitBucket. If myBranch does not exist in the remote repository, then it will be created.

The next time you push, you can use the same command, or you can set things up so git push will push only the current repostory:

git config push.default current

You can also:

git push --set-upstream origin BridgeReader04

Suppose you and another person are working on the same repository and the other person adds a branch and pushes it to the origin. When you run git pull, that will pull down the new branch. You can see it by typing git branch -a. To switch it to it, just type git branch , where NewBranch is the name of the new branch the other person added.

git pull
git branch -a
git checkout myBranch

Clone a Local Repository

git clone file:////home/charlie/git/myrepo git clone file:////$HOME/git/myrepo

Merge from a local repository. For instance, you are in master, and you want to merge the commits that are in working:

git merge working

Clone a Specific Branch

To clone a specific branch, got to a directory that does not contain a repository with the same name as the repository you want to clone. Then enter the following command:

git clone -b <branch> <url>

For example:

git clone -b mybranch git@github.com:username/myrepo.git

More specifically:

git clone -b test git@bitbucket.org:charlie/myrepo.git

Here is another example, working with local files:

BRANCH=chasyte
REPOSITORY=file:////home/charlie/git/myrepo
mkdir
cd $BRANCH
git init
git remote add -t $BRANCH -f origin $REPOSITORY
git checkout $BRANCH

Git Checkout a Specific Branch

Sometimes you want to just look at the state of your repository at some point in the past. You don't necessarily want to switch to that state, but just view it. You can do that by checking out the repository from the state it was at after a particular commit.

Let's assume you have just done a pull, and have the whole up to date repository on your machine. Now you want to check out the repository and examine a branch that mirrors the way your project looked at some point in the past.

Every time you do a commit, there is a funny number associated with that commit. You can easily find these numbers, often called SHAs, on the Git web sites. Typing git log can also help you find these numbers.

Now just do a checkout, passing in that number:

git checkout -b charlie01 a608f0e

In this example we created a new branch called charlie01 that mirrors the state of the repository as it looked after the commit identified by the SHA a608f0e.

Find Old Version of Code

The subject of checking out a specific branch is so important, that it might be worth discussing in more depth. When you do good work, check it in. Then later, if you find that you wish you could go back to the working verison of your code, there are several things you can do. Here is one.

One way to get access to the older version of your code is to check it out in a branch. To do this, you need to know the commit number of the good code. Often the best way to find this number is to browse on GitHub or BitBucket. But you can also find with with the Git log comment:

git log

Or

git log <FileName>

Sample output:

>git log Control.js
commit bbe914c7347c51237b21a4b9a68be29fac3fccf4
Author: Charlie CedarIsle Calvert <charlie@elvenware.com>
Date:   Tue May 26 22:07:44 2015 -0700

    Week08 Initial checkin

commit 16763f8a3327b074d56f7cb563900ae2d34729a6
Author: Charlie CedarIsle Calvert <charlie@elvenware.com>
Date:   Mon May 25 16:20:49 2015 -0700

    Midterm updated getScientists method with support for Jade files

When reading the results of this log, you will begin to understand why it is so important to type in something useful when you commit your code. A useful comment can help you find a particular older version of your code.

Now create the branch based on the commit you did after we worked together. Here I call git branch and give a name for the branch and the full commit number as seen above in the log statement:

git branch initial bbe914c7347c51237b21a4b9a68be29fac3fccf4

Now checkout the branch:

git checkout initial

Now you have a copy of the code from the past that you wanted to see. You might save the portions you need into another directory. Don't just open it in an editor, copy it someplace else outside your repository. Then (AND IMPORTANTLY), go back to the master branch:

git checkout master

You need to go back to the master because you probably don't want to do your work in the older branch as it does not contain all your recent work. You just wanted to look at your older files, save the parts you wanted, and then go back to your new work.

Again, there are other ways to solve this problem. This one, however, can be useful as it:

Delete Branch

Having a branch in your code is not nearly as expensive as it might seem at first. If, however, you want to delete it. First switch to master, and then:

git branch -d initial

Replace Master with Old Commit

Sometimes you will find that the current master branch is no longer what you want. You want to go back to some earlier commit, some earlier state. The first thing to do is find the commit number of that old branch as described above. Suppose that number is f631de7. Here is how to replace the current master branch with that older commit. You may or may not want to start with a git fetch to to pull down the most recent code (current master branch) from your repository. At any rate, once you are ready to begin, you should do this, where the first line creates a branch called yesterday based on specific commit:

git branch yesterday f631de7
git checkout yesterday
git merge -s ours master
git checkout master
git merge yesterday

The -s stands for strategy: git merge --strategy ours master. It says that if there are conflicts during the merge, always prefer our current branch over the master branch.

When looking at the above code, it should be clear that you can use this same strategy to replace the current master with some any random branch. Suppose you want to replace the current master with the a branch called goodbranch:

git checkout goodbranch
git merge -s ours master
git checkout master
git merge yesterday

Forking

Frequently we do not own a repository, yet we want to work with the code in the repository. We could clone the repository, but then when it is updated, the only way to get the changes is to risk over writing or losing our own changes. We could make a separate copy of the repository, but then we would never be able to get updates. There is, however, a third solution: fork the repository. Now you own a copy of the repository. You can update it all you want. At the same time, there is still a link (upstream) back to the original repository. You can make your changes, and still merge in any changes from the original. You can't contribute to the original, but you merge in changes from the original. If you ever wanted to merge your changes to the original repository, then there is a way to do that. In other words, this is how many open source accept changes. You fork their project, develop new code, then tell the owners of the original repository to look at your fork, and if they like it they can accept the changes.

Upstream Repo    --->     My Fork
          \                <
           \              /
            \            /
             >          >
            My Local Copy

In the diagram above, the UpStream Repo is the Original Project that we want to fork. When we fork the repository, then we end up with a copy of the repository on GitHub. That is what I label as My Fork. We can then make a local copy of My Fork.

The path between My Fork and My Local Fork is two way: we can pull and push. The path between UpStream and My Local Copy is one way: you can only pull. That is, you can only pull unless the Upstream repo decides to accept your commits, which is a special case.

Most of the time, we don't want to merge our commits with the original repo, we just want to modify our version of the project without loosing the link to the original. So we are usually only concerned with pulling from the UpStream Repo. Therefore, I do not show on this diagram that it is possible to get our changes back into the upstream repository. That is a special case, and not shown in this diagram.

The commands you give are as follows, though you only give the first command once, the first time you try to update your local copy from the upstream remote.

git remote add upstream [GIT URL OF SOURCE REPO]
git fetch upstream
git rebase upstream/master

For instance, if you are trying to get updates from Angular-Seed, the first command would look like this:

git remote add upstream https://github.com/angular/angular-seed.git

And a first session might look like this:

>git remote add upstream https://github.com/angular/angular-seed.git
>git fetch upstream
From https://github.com/angular/angular-seed
 * [new branch]      master     -> upstream/master
 * [new branch]      v0.10.x    -> upstream/v0.10.x
>git rebase upstream/master
Current branch master is up to date.

After you added the remote, you can skip that step, so the session might look more like this:

>git fetch upstream
>git rebase upstream/master
Current branch master is up to date.

Exactly what you see will depend on what changes have occurred to upstream remote repository. In the examples I have shown, the upstream repository and the local repository were identical, so the output is not very interesting, but hopefully it helps you see what you need to do.

VonC has a number of good explanations on StackOverflow:

Fix Broken Fork

Stackoverflow: How to update git forked repository

git fetch upstream git checkout -f -B master upstream/master

Rebase

I use rebasing when I have two branches, and sometimes want to update a particular branch with changes from the other branch. For instance, I use rebase in this scenario:

Here is what I do. I create two branchs:

Here is how it works:

Now I have the most recent copy of the student's repository in the master branch. If I want to update mycopy with the most recent changes from the student's repository, I:

There may well be conflicts at this point, but Git talks you through the process of fixing them. Just edit the files that need to be fixed up. See the section in this document on merging code if you need help on this. Then continue the rebase:

The end result is that I have two copies of the students code:

I think it is obvious from the above, but it is probably worth saying anyway. When I work on a student's code, I only use mycopy. So I spend 90 percent of my time in mycopy. I only switch back to master in order to pull the most recent version of the student's code. When I want to update my copy based on the most recent version of the student's code, I switch to the mycopy branch, and then rebase.

Restructure you Repository

If you want to restructure a Git repository, you can usually do what you want with the following command:

git mv X Y

Where X and Y could be either the name of a folder or a file. Examples:

git mv MyFile.txt MyFolder/.

mkdir MyNewFolder
git mv *.txt MyNewFolder/.
git mv MyFolder MyNewFolder/.

Just as on Linux, you can rename a file with the mv command:

git mv MyFile01.txt MyFile02.txt

This renames MyFile01.txt to MyFile02.txt.

If you want to delete an entire directory and its subdirectories, try this:

git rm -r MyFolder

To delete a folder from the repository, but keep it on your local machine, do this:

git rm -r --cached folderName

For instance, suppose you have accidentally committed something like a node_modules folder to the repo that you want to keep on your local drive, but remove from the repo. Do this:

git rm -r --cached node_modules

And so on...

The point being that you don't have to start a new repository if you want to change the way you have set up your directories. Just use git mv or git rm to delete files.

git rm

![git rm](https://drive.google.com/uc?id=0B25UTAlOfPRGaVR1dVFGTEstMDQ)

Permanent Delete


In the following explanation I focus on permanently removing a folder called node_modules from your repository. However, this explanation applies to removing any folder or repository.

NOTE: There are two ways to permanently remove files or folders. Below you can find a discussion of how to do the delete using a tool called BFG.

If you did not include node_modules in your .gitignore file. As a result, your repository has a lot of deeply nested files in the director called Week02-GetNumbers/node_modules. As a result, your repository is many times bigger than it needs to be, and I cannot check it out on Windows, though I can on Linux. I can't check it out because the path to the files in this deeply nested directory are too long for Windows to handle.

You should immediately update your .gitignore file in the root of your repository so that it includes node_modules. If you have any questions, on how to do this, please ask in the discussion area.

The next step is to permanently remove the node_modules folders. This is an odd thing to do, because the whole purpose of Git is to keep track, for all time, of all changes that you make to a project. So even if you delete a file from a repository with git rm file_name or git rm -r node_modules, it should still be in the repository, but not in your working copy of the files. In other words, node keeps track of the files so that you can recall them if you need them.

It is, however, possible to permanently remove a file or folder from a repository. Doing so is a rather drastic step. Here is the command you should run from the root of your repository:

git filter-branch --tree-filter 'rm -rf Week02-GetNumbers/node_modules' HEAD

More in-depth explanations can be found at these sites:

After you do this, you should push your repository like this:

git push origin master --force

Use that syntax just the one time. After you have pushed it once, you can resume pushing the repository with a simple git push command. When you are done, let me know about the change by resubmitting the assignment. I will then re-clone your repository.

This drastic step breaks all other copies of the repository. On your end, you should therefore delete any copies of the repository from all your machines, and then re-clone it. (I think you can keep the one on which you issued this command and the push, but all other copies should be deleted.) Don't try to run the command on all copies. That won't work. Instead, delete them, and then re-clone them with this command:

git clone git@bitbucket.org:lastname/prog272_lastname.git

It is difficult to emphasize too often how very important it is to put node_modules in your .gitignore file. Please look on GitHub, BitBucket, etc when you are through with this process and make sure it is now included.

NOTES: I've heard it said, but not tested that you can remove multiple copies of nested folders like this:

git filter-branch --tree-filter 'git rm -rf --ignore-unmatch */foo/* ' HEAD

Git mv vs Bash mv

There is usually not a lot of difference between git mv and mv. Nevertheless, I suggest you use git mv and git rm when renaming or deleting folders that are already under git control.

Though it is not always a problem, there are cases when using git mv rather than just mv is better able to preserver your history. Every time you commit, git saves the state of your file. So it remembers what a file looked like the first time you committed it, and each subsequent time. If you use mv instead of git mv, then git can think that you simply deleted one file and created a new file. Then you history won't be attached to the new file, and you can see what it looked like in the past. If you use git mv then your history for a file will be preserved.

Usually, however, git can be smart about what happens when you use mv instead of git mv. In other words, it seems to me, and I am not certain about this, that it can usually figure out that a deleted file and a new file are so exactly similar that you must have done a rename, and so it preserves the file history. But I don't think it is good idea to count on this, instead use git mv to be safe.

At first, you won't care much about the history of files since you won't really understand how to go back in time to previous version. But over time, you will learn how to do this. Then you may find occasions when you are glad you preserved the whole history of a file. But frankly, this topic is complicated, and I would take what I say here with a grain of salt. All I can really assert is just that it is safer and easier to use git mv rather than mv.

See this discussion:

http://stackoverflow.com/a/1094392/253576 (Links to an external site.)

Notice that the guy says they are the same, but the top comment on his reply is that "git mv" has a few safeties built in. The discussion goes on from there, but I don't want to try to recap it. It gets pretty technical pretty quickly.

Incremental: One Step at a Time

I see students get in trouble by renaming a lot of files, and especially entire folders, then not immediately committing their work. Sometimes they even delete or rename a whole project folder, and then recreate a new one with the same name:

rm -r Week0X-SomeProject
mkdir Week0X-SomeProject

This formula can lead to chaos. If you want to do something like this, I would do it like this:

git rm -r Week0X-SomeProject
git add
git commit -m "Deleting hopeless messed up folder.

And, depending on circumstances, sometimes try a git push and git pull after the above to make sure all is still okay.

Then: mkdir Week0X-SomeProject or the equivalent, which might be a copy operation or a run of CreateAllExpress etc.

The point being that one should work one step at a time. Don't make dozens of changes at once without seeing if you can successfully commit and possibly also push and pull.

The same is true in your code, don't make dozens of changes at once without checking to see if your tests still pass and your program runs. If you must make those changes very quickly because you are following along in class and have to move to the next step, then when you are done for the day, comment out nearly all the steps, leaving only the first one visible. Get it to work, then un-comment a bit more of your work, get it set up correctly, etc. Always one step at a time!

Permenant Delete with BFG

A tool called BFG can make permanently deleting files from a repository fairly easy. Do this if you accidentally committed something with a password in it, or some huge files or directories (node_modules, .metadata) that you don't want.

Download the bfg jar file from here:

Now create a mirror of your repository in a new directory:

git clone --mirror git@github.com:charliecalvert/Test.git

Run BFG on to, for instance, remove all node_modules folders:

cd Test.git
java -jar c:\src\bfg-1.11.1.jar --delete-folders node_modules

Get rid of unneeded files:

git reflog expire --expire=now --all
git gc --prune=now --aggressive

To confirm that your work succeeded, clone the mirror:

cd \temp
mkdir myclone
cd myclone
git clone C:\temp\Test.git

Now you will have regular repository and can explore it.

When you are convinced that all is good then go back to the mirrored repo and push it:

git push

Your old repositories are now out of date and need to be cloned again. Don't just update your repository, clone it a second time:

ren Test Test.old  
git clone git@github.com:charliecalvert/Test.git

Git Deletions and Workflow

In general, I find one of the gotchas with using Git is that deletion and renaming of files is best done with Git. So most of the time I'm working happily in Eclipse, making lots of edits and creating new files. I do all this work in Eclipse without problem. Life is good. Then I decide I want to rename a file, move it into a new directory, or delete it. At that point, I tend to switch to the command line, and give these commands with git:

git rm MyFile.js

git mv MyFile.js MyNewName.js

The first command deletes a file, the second renames it. Here is removing (entirely deleting) a directory and its contents (obviously a dangerous command):

git rm -r MyDirectory

Then I choose Alt-Tab to go back to Eclipse, and generally it has found my changes already. If not, I focus the Project Manager and press F5 to refresh the view. As I say, this is perhaps less than ideal, but it is my general workflow when in a Git Repository.

The point is that Git will track the changes we make that way, so if we need to restore a deleted file, etc, we can do so later on. Just trust Git to keep all your changes.

Undo an Add in Git

Sometimes you do an add by mistake:

git add MyFile.txt
git add YourFile.txt

Suppose that you meant to one of the adds, but not the other. You can fix it like this:

git reset HEAD MyFile.txt

And this resets everything to before the add:

git reset HEAD --

Don't Nest Repositories

You should always have a mental image of the directory tree in which you do your work. This can be important when you are creating a new repository. One mistake that new comers often make is to nest one git repository repository inside another. For instance, suppose you have a repository here:

d:\src\git\Repo01

In most cases, you don't want to add another repository inside this existing repository:

d:\src\git\Repo01\Repo02

It is not illegal to do this, but it is not likely that you did it on purpose, and once you have made the mistake it is hard to undo.

You can fix this error, but you don't want to face this problem. Either keep a clear mental image of your file directory structure, or use the Windows Explorer to review your file structure before you create a new repository. In general, a good plan is to create a folder called Git or GitHub, and then create all your repositories as children of this folder:

c:\git\Repo1
c:\git\Repo2

Alternatively:

c:\users\me\documents\git\Repo01
c:\users\me\documents\git\Repo01

It is, of course, okay to complex directory structures inside any one repository. The mistake is not nesting directories, but nesting repositories. For instance, the following is fine:

C:\git\Repo1
c:\git\Repo1\Dir01
c:\git\Repo2
c:\git\Repo2\Dir01
c:\git\Repo2\Dir02

Terminal is Not Fully Functional

You can get ride of this warning by defining an environment variable called TERM and setting it to msys.

On Windows, you can type:

set TERM=msys

Or edit the Environment Variables for your Account and create a new variable called TERM set to the msys.

On Linux, you export TERM = msys

export TERM=msys

Git Deletions and Workflow

In general, I find one of the gotchas with using Git is that deletion and renaming of files is best done with Git. So most of the time I'm working happily in Eclipse, making lots of edits and creating new files. I do all this work in Eclipse without problem. Life is good. Then I decide I want to rename a file, move it into a new directory, or delete it. At that point, I tend to switch to the command line, and give these commands with git:

git rm MyFile.js

git mv MyFile.js MyNewName.js

The first command deletes a file, the second renames it. Here is removing (entirely deleting) a directory and its contents (obviously a dangerous command):

git rm -r MyDirectory

Then I choose Alt-Tab to go back to Eclipse, and generally it has found my changes already. If not, I focus the Project Manager and press F5 to refresh the view. As I say, this is perhaps less than ideal, but it is my general workflow when in a Git Repository.

The point is that Git will track the changes we make that way, so if we need to restore a deleted file, etc, we can do so later on. Just trust Git to keep all your changes.

Undo an Add in Git

Sometimes you do an add by mistake:

git add MyFile.txt
git add YourFile.txt

Suppose that you meant to one of the adds, but not the other. You can fix it like this:

git reset HEAD MyFile.txt

And this resets everything to before the add:

git reset HEAD --

Merging Code

If the same file is edited in two different instances of your repository then the two versions of your file need to be merged. Much of the time, git can merge the two instances automatically. Problems can occur, however, especially if the same line is edited in two different instances of your repository.

NOTE: By two instances of your repository, I mean a scenario like this: one instance might be the your version of your repository at home, and one might be the version of your repository at school. Also consider the case where a team is working together, and team member A and team member B both edit the same file more or less at the same time. If you think about it, you can see that merge conflicts cannot occur if we commit and push our work before we start editing it at a second location. If you push at school before you go home, and pull when you get home, then a merge conflict cannot occur. If team member A pushes his work before team member B pulls and starts editing the shared file, then no conflict can occur. Finally, it is not an error to have a merge conflict. Git is designed to let two people work on the same file at the same time. You just need to understand merge conflicts, and how to resolve those conflicts that git cannot resolve automatically.

If Git tries to merge versions of your code and cannot do so neatly, you can end up with code that has funny symbols in it, like this:

<<<<<<< HEAD
>>>>>>> 89e8d1f35ea5f393b3e5830d7169e071695b1cca

This means that Git could not cleanly merge two files. Instead, it puts both versions of the disputed code in your file, and leaves it to you to sort it out. In this case you might see a message like this:

$ git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 2), reused 3 (delta 2), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:charliecalvert/prog270-calvert-2016
   944f6b5..f290612  master     -> origin/master
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

When we open up README.md in an editor such as geany, we might see this:

<<<<<<< HEAD
# Charlie Calvert's Programming 270 Repository
=======
# Charlie Calvert's Prog 270 Repository
>>>>>>> f2906125f41326c09715b178cfc832e0e1ae0800

Here is where I'm putting some of my files from the Prog270 Winter, 2016 class.

This occurs because you had two different versions of README.html that you checked into two different repositories. For instance, you made changes at school, checked them in, then pushed. Then you went home and did not pull the latest changes. Instead, you started making changes at home, checked those in, and tried to push or pull. Suddenly you have two different versions of the same file. They need to be merged, and Git does the best job it can. In this case, that was less than perfect, and you need to edit the file, and merge the two versions yourself. The versions are separated by code that looks like this:

<<<<<<< HEAD
YOUR Version 1 here
=======
YOUR Version 2 here
>>>>>>> 89e8d1f35ea5f393b3e5830d7169e071695b1cca

NOTE: The HEAD is the current version of your repository, and the long string of numbers and letters is the commit ID of some previous version of your repository.

More specifically, in our case, the part of the file with a conflict looks like this:

<<<<<<< HEAD
# Charlie Calvert's Programming 270 Repository
=======
# Charlie Calvert's Prog 270 Repository
>>>>>>> f2906125f41326c09715b178cfc832e0e1ae0800

We can now merge the two instances manually, which means that we can decide which version we want, or we can come up with some third alternative. In this case, I'll merge the lines like this:

# Charlie Calvert's Prog 270 Class Repository

Zooming out to see the entire edited file, it now looks likes this:

# Charlie Calvert's Prog 270 Class Repository

Here is where I'm putting some of my files from the Prog270 Winter, 2016 class.

Note that during the edit I removed the HEAD and funny looking long commit number. They are no longer needed, so I deleted them and came up with a merged copy of the two different versions of the file held in git.

All is now well. We can add, commit and push/pull, and life returns to normal. It all may seem a bit complicated at first, but frankly, I think that Git chose a simple and intuitive way to handle this whole issue.

Merge Tool

Note that you can also use mergetool to help you with the process of merging two files.

Open up your ~/.gitconfig in geany:

$ geany ~/.gitconfig

The email, name and push default fields should already be filled out. Add a third item called diff tool and set it to meld. Of course, you can only use meld if you are on a GUI (non-server) instance, and if you have it installed. There are other options you can use, but you will need to research those on your own.

[user]
    email = charlie@elvenware.com
    name = Charlie on RohanElf
[push]
    default = simple
[diff]
    tool = meld
[merge]
    tool = meld

To insert this automatically, try:

git config --global merge.tool meld

Or on Windows:

git config --global merge.tool "meld"
git config --global mergetool.meld.path "C:\Program Files (x86)\Meld\Meld\Meld.exe"

NOTE: Besides meld, your options include: opendiff kdiff3 tkdiff xxdiff tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc codecompare emerge vimdiff.

Now, when you hit a conflict, use mergetool:

$ git mergetool

Merging:
README.md

Normal merge conflict for 'README.md':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (meld):

mergetool will create a file with a name like this README.md.orig as a backup file. Feel free to delete this file if you sure the merge has completed successfully.

Merge Code Example Two {merge-code-two}

Here is a second example of the contents of a file that needs to be manually merged:

<<<<<<< HEAD
<!DOCTYPE html>
<!-- MidTerm-CanvasGrid --
< -- Prog 282                         --
< -- Spring 2013                      -->
<html>
    <head>
        <title>Get User</title>
    </head>
    <link href="buttons.css" rel="stylesheet" type="text/css">
<body>

<form method="get" action="/authenticate">
    <p>Login using OpenID</p>
    <img src="../Images/Openid-16x16.gif" alt="OpenId Image" >
    <input name="openid_identifier" />
    <input class="myButton"  type="submit" value="Login" />
</form>

</body>
=======
<!DOCTYPE html>
<!-- MidTerm-CanvasGrid  --
< -- Prog 282                         --
< -- Spring 2013                      -->
<html>
    <head>
        <title>Get User</title>
    </head>
    <link href="buttons.css" rel="stylesheet" type="text/css">
<body>

<form method="get" action="/authenticate">
    <p>Login using OpenID</p>
    <img src="../Images/Openid-16x16.gif" alt="OpenId Image" >
    <input name="openid_identifier" />
    <input class="myButton"  type="submit" value="Login" />
</form>

</body>
>>>>>>> 89e8d1f35ea5f393b3e5830d7169e071695b1cca
</html>

You can fix all this by editing the files and getting something like this:

<!DOCTYPE html>
<!-- MidTerm-CanvasGrid  --
< -- Prog 282                         --
< -- Spring 2013                      -->
<html>
    <head>
        <title>Get User</title>
    </head>
    <link href="buttons.css" rel="stylesheet" type="text/css">
<body>

<form method="get" action="/authenticate">
    <p>Login using OpenID</p>
    <img src="../Images/Openid-16x16.gif" alt="OpenId Image" >
    <input name="openid_identifier" />
    <input class="myButton"  type="submit" value="Login" />
</form>

</body>
</html>

The disputed code in a file may be only one line long, as in our first example, or it may be nearly the entirety of a file as it is here. You only need to merge the part that is in dispute.

As a rule, if one part of a file is edited in one place, and a second part of a file is edited in second place, then Git can successfully merge the two versions without producing code like that shown above. However, if the same line, or lines, are edited in two different places (usually by two different people, or the same user in two locations) then there is disputed code that must be merged manually.

When working on these kinds of problems you may see an error message stating that "You may want to first integrate the remote changes before pushing again. See the note about fast-forwards..."

You perhaps understand that the error means that you have to do a git pull, then a git push, in this case.

git pull
git push

You have to do this because the code in the remote GitHub repository is "ahead of" the code in your local repository. In other words, it has changes that have not yet been incorporated into your local repository.

Suppose you and a friend are working on a document called Foo. Your friend has made changes to Foo and checked them in. Now you have your own copy of Foo, and you want to check it in. But if you did that would overwrite his changes. So you have to first pull his changes into your current copy, fix any conflicts that might need to be made after the merge, and then push your copy.

Why is it that you have to perform the merge? Why can't you push your code into the GitHub repository and make the merge there? Because a merge might result in errors. What is in the GitHub repository (the origin) is the canonical version of the code. It should always work. You can't risk creating errors there, but you can risk errors in your local repository. So you pull his changes down, make sure your changes and his changes (the merge) work together, and once you have manually ensured that all is good, then you can push to the main repository.

Git Tag

To see the tags:

git tag

To make a tag called v0.1:

git tag v0.1

To add a comment (annotation) to your tag when you make it:

git tag -a v0.1 -m "This is the beginning state"

Notice the -a, which stands for annotation

You can view information about a tag like this:

git show v0.1

Your tag is local unless you explicitly push it like this:

git push origin v0.1

On GitHub, you can go to the main page for your repository, click the button that says Branch master, and switch to the tags page. You will see your tags.

Understanding Tags

A tag marks the state your repository was in at the time you tagged your files. It doesn't have anything to do with pushing files, it just marks a place in your repository.

If you add, commit, push, and then tag, that tag will point to the place in your repository where you pushed that last set of files.

If you tag, then add, commit and push, your tag will point to the place in your repository just before you pushed.

If you tagged a repository at 3 PM on March 5, 2017, then you can get back to the way your repository looked at that point in time by creating a branch on that tag.

By the "place in your repository", I mean the point in the time-line of your repository at which you tagged your repository. Among other things, it says: "if you want to see what my repository looked like at the time I tagged it, then create a branch based on this tag."

Miscelaneous Information

I've not had a chance to sort this out yet. You can just ignore most or all of what is written in this section.

Learn about NodeJS

https://www.windowsazure.com/en-us/develop/nodejs/

npm install azure node-uuid DSInit /sqlInstance:.

SimpleDb

https://github.com/rjrodger/simpledb

Templating engines

wiki-templating

This usually means that you don't have a default file set for your application. Try explicitly naming the file:

http://localhost:81/server.js

BitBucket Snippets

Post snippets to BitBucket

    #! /bin/bash

    # This gets a specific snippet
    curl https://api.bitbucket.org/2.0/snippets/ccalvert/i7pn/files/b8e99e91b0419115ac14f79ba9b3d29c6bc5446d/InstallNodePackages.sh

    # This gets information on me and my BitBuckSnippets
    # url https://api.bitbucket.org/2.0/snippets/ccalvert/

    # This gets information on a specific shippet
    # curl https://api.bitbucket.org/2.0/snippets/ccalvert/i7pn

Clone a snippet repository:

git clone git@bitbucket.org:snippets/ccalvert/i7pn/installnodepackages.git

Maintain Repository

Every little bit, run:

git gc

Verify it:

C:\Git\repo>git verify-pack -verbose .git\objects\pack\pack-4beeb...idx

Find big files that you don't Need

Run git gc per above.

Now find any huge files:

git rev-list --objects --all > bar.txt

Look for entries about your big file:

git log --pretty=oneline -- /Tech/Monkey.png

Like this:

C:\Git\repo>git log --pretty=oneline -- Tech/Graphics/HtmlSwitch/Monkey.JPG
3c6a55fa8d507e31864e5651ca1fec3a9eb2b5d1 delete big files
69d6f1ed3de5198b9595d2edae818bb00ef4444d Adding switch examples

git filter-branch --index-filter "git rm --cached --ignore-unmatch Tech/Graphics/HtmlSwitch/Monkey.JPG" -- 69d6f1^

Or:

git filter-branch --index-filter "git rm --cached --ignore-unmatch Tech/Graphics/HtmlSwitch/Monkey.JPG" -- 69d6f1^

Or:

git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch Tech/Graphics/HtmlSwitch/Monkey.JPG" HEAD

At this point you ought to be able to push your changes.

Linux or Mac users, See this GIST:

Undelete Something

You can use git checkout to undelete things. For instance,

git checkout Week07-TwitterInteractive

This should undelete a directory or file if you have not yet done a commit. If you have done the commit, then run git log so you can get the id of the commit you want to restore. Then try something like this:

git checkout <COMMIT_ID> README.md

Here is a few lines of a session that uses the git log command:

$ git log --pretty=oneline
747a7b0ab64cf3b9a3fddbcc9cd8343aef3f7219 Finished TwitterInteractive
09c0818092c7e4c25852a44ab9ff81efafe31214 finished bitlyRefine
9e613644dd3ce268d0f763810d90e8941bd81474 ran grunt clean

And here's how to get a file back to third commit shown above:

git checkout 9e613644dd3ce268d0f76 README.md

References:

Script It

I can be impatient at times, but I've learned the hard way that rushing certain steps, or skipping certain steps, can end up causing me to spend a lot of time later trying to fix something that I broke through rushing or carelessness.

Sometimes I also take a moment to write a script that can help me automate certain steps. Running the script takes just a moment, yet the script can help me make thorough checks and avoid careless errors.

For instance, I maintain a lot, probably too many, repositories. So I've written the following script that checks each of the repos and lets me know if I have not pushed my most recent code. Then, I have a corresponding program that checks to make sure I've pulled all my most recent code so that I don't start working on code that is out of date. Things like cn and i3c are aliases that I use as shortcuts to navigate to a repository. For instance, here is i3c:

export GIT_HOME=$HOME/Git
alias i3c='cd $GIT_HOME/isit322-calvert-2017'

And here is the script:

#! /bin/bash


shopt -s expand_aliases
source ~/.bash_aliases

alias

function title() {
 echo -e "\n==============="
 echo $1
 echo "==============="
}

function winter() {
 title 'i3c Working Site'
 i3c && git status
}

function spring() {
 title 'isit322-calvert'
 i3c && git status

 title 'Prog272Calvert'
 p2c && git status
}

function fall() {
 title 'isit320-calvert'
 i3c && git status

 title 'Prog270Calvert'
 p2c && git status
}


title 'Git IO (charlie calvert)'
gitio && git status

title 'Cloud Notes'
cn && git status

title 'JsObjects'
jo && git status

title 'Writing'
wt && git status

title 'Elf Site'
elfsite && git status

title 'Elven Tools'
elventools && git status

title 'Elven Assignments'
elfa && git status

spring