Git

Wait, what?? Why did Git show up in a citrix website?

As a part of my job, I write lot of powershell and batch scripts. Everytime I write a script, i add more code later on, and it is very difficult to manage versions of each script. So, i started using git and it works great!! so thought to share my git knowledge with you all.

About Git:

Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.

When you are writing scripts, for the very first time, you add some code and make it work. After sometime, you like to add more functionality to your script and you add more functions or add more code to your script. If you want to maintain them locally, you would end up multiple files with different names as version1, version2 and so on.. But Git handles this issue very efficiently. Every time you pull the file, you would get latest file and you can have historic data on when and who changed the script or code.

Commands:

Git init demo – to initialize git repo called “demo”. You can name it as whatever name you like.
Working directory – contains all the files and folders for your application(or the script)
Staging Area – you add file and not pushed yet. or changes are made to file, but not committed yet.
Git Repository – contains all the saved changes and modified/updated files and commit history.


Git index:

  • The git “index” is where you place files you want committed to the git repository.
  • The index is also known as cachedirectory cachecurrent directory cachestaging areastaged files.
  • Before you “commit” (checkin) files to the git repository, you need to first place the files in the git “index”.
  • The index is not the working directory: you can type a command such as git status, and git will tell you what files in your working directory have been added to the git index (for example, by using the git add filename command).
  • The index is not the git repository: files in the git index are files that git would commit to the git repository if you used the git commit command.

Global parameters:

You can specify global parameters like your username and email. This information will be used whenever you commit. Ex, setup your name and email as:
git config –global user.name “ctxadmin”
git config –global user.email “Your email”

More info about global parameters here: https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup

Git Remote:

Git remote is used to work with remote git repositories like github, bitbucket etc. Use git remote to pull your repository to your local machine and have a connection between your local scripts and push them to your remote repository.

  • To add remote repository use: git remote add origin https://ctxadmin@bitbucket.org/GitUsername/projectname.git

Goto your git folder and execute, git remote -v to see the remote branches associated to our local git repo.

  • You can also clone your existing repository and start working on files locally, and push them to your remote repo. Use below command to do so.
    git clone https://ctxadmin@bitbucket.org/GitUsername/projectname.git # enter username and password when prompted
  • git status – to check the current status or current situation of your repository. Note: you should be in git repository folder for this command to give the correct output.

Differences between git clone and git remote:

git remote adds a reference to a remote repository for further tracking, while git clone, well, clones locally a remote repository.

In git remote, you dont have all the files in your project( ie., remote repo). You just have a link between your local git folder or repo and remote repo. You can push all the files to your remote repo.

In git clone, you copy/clone your remote repo completely, make changes locally and push them to remote repo(ie., your project in github or bitbucket etc). Check below link for more info:

https://www.quora.com/What’s-the-difference-between-git-remote-and-git-clone

create a new repository on the command line

echo "# testproject" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/ctxadmin/testproject.git
git push -u origin master

…or push an existing repository from the command line

git remote add origin https://github.com/ctxadmin/testproject.git
git push -u origin master

.md means markdown which is file extension for git files. Git has its own github flavoured markdown – which basically tells you how to to write code in github, syntax and structure of github code.  https://help.github.com/categories/writing-on-github/

To create a new file, using your favourite editor, use – notepad README.md. This would create README.md file in your demo folder. If you use git status now, it shows different output than earlier. It says there is an untracked file, README.md. And it says use “git add ” command.

So far our README.md file is in working directory, and using “git add README.md” moves this file from working directory to staging area. Use git status to see the status of this transaction.

To commit this file, use git commit -m “First file in this demo project”. Use git status to check status again. In your git folder, .git is the folder which maintains all the git scripts and git files/folders. These are git files and not related to the files or folders that you use in your git repository. If you remove this .git folder, you won’t be able to execute any of the git commands.

In case if you want to remove, use ‘rm -rf .git‘ to remove .git folder. None of git commands work. To reinitialize, use “git init .” – This would reinitialize git in the current folder. In our case, if you have removed demo folder completely, then you could say git init demo. But our demo folder exists, just want to reinitialize git in demo folder, so use “git init .” NOTE: If you remove .git folder, you have to re-add all your files to git again. Newly initialized git folder doesnt know what files are there and what is their version history. You have to re-add all your files again.

  • To add all files in a folder to git repository, use “git add . ” This would add all the files in current folder to git repository.

Files which are in git working directory but not added to git repository, are shown as red color when you use “git status” command. ** Now they are in working directory.

Files which are added to git repository but not committed are shown as green, when you use “git status” command. **Now they are in staging area.

To commit multiple files, add all the files and say git commit (press enter ) or git commit -m “save description for the multiple files”

  • Use “git log” or “git show” to see all the commits. NOTE: if you have removed .git folder, all your earlier commit information will be lost.

If you have modified file in your git working directory, and a new file added to this folder, when you use “git status” it shows the files details as modified for one file and untracked for other file.

  • use “git ls-files ” command to see what files are already moved to git repository. Ex, you have two files and If you add a new file to git working directory, and then use “git ls-files” command, it will only show 2 files as the 3rd file is not pushed to git repository yet.

If you edit a file in working directory, you need to add that file again to git, and then commit.

Ex, if you have readme.md file in your working directory and git repository and you modify it, you have to use below commands to push the modified readme.md file to git.

git add readme.md

git commit -m “Modified readme file”

But you can use git commit -a to add the file and save it to git repository directly or git commit -am “commit message”

Reset added file to git:

Modify filename.txt and use “git add filename.txt” to add it to git.  Now to remove this file and reset it to last saved file in git,

git reset HEAD filename.txt” – to remove this changed file from git. You pushed the updated/changed file to git, but didnt commit yet. Using above command will only remove the file from git.

git checkout — filename.txt” – to remove any udpates you made to the file and reset it back to last saved in git. For example you have one line in the file, and saved it to git. Now you add two more lines, and added it to git, but didnt commit. When you use reset command, it will remove file from git and When you use checkout command, it will remove the new content that is added to the file. So that your file will have only one line.

  • Reset command to remove file from git
  • Checkout command to reset file back to original content.

There are 3 types of reset in git: Soft, Mixed, Hard. Mixed is default.

When you modify a file in your repository, the change is initially unstaged. In order to commit it, you must stage it—that is, add it to the index—using git add. When you make a commit, the changes that are committed are those that have been added to the index.

git reset changes, at minimum, where your current branch is pointing. The difference between –mixed and –soft is whether or not your index is also modified. So, if we’re on branch master with this series of commits:

– A – B – C (master)

HEADpoints to C and the index matches C.

–soft

When we run git reset –soft B, master (and thus HEAD) now points to B, but the index still has the changes from C; git status will show them as staged. So if we run git commit at this point, we’ll get a new commit with the same changes as C.

–mixed

Okay, so starting from here again:

– A – B – C (master)

Now let’s do git reset –mixed B. Once again, master and HEAD point to B, but this time the index is also modified to match B. If we run git commit at this point, nothing will happen since the index matches HEAD. We still have the changes in the working directory, but since they’re not in the index, git status shows them as unstaged. To commit them, you would git add and then commit as usual.

–hard

And finally, –hard is the same as –mixed (it changes your HEAD and index), except that –hard also modifies your working directory. If we’re at C and run git reset –hard B, then the changes added in C, as well as any uncommitted changes you have, will be removed, and the files in your working copy will match commit B. Since you can permanently lose changes this way, you should always run git status before doing a hard reset to make sure your working directory is clean or that you’re okay with losing your uncommitted changes.

From <https://gist.github.com/tnguyen14/0827ae6eefdff39e452b>

View all commits:

git log –oneline –graph –decorate –all : View all changes.

Git show “commit number” (we can get commit number from above command) : To see changes in any single commit. Ex, git show ff1e54e

Create Alias in Git:

To create alias for command, git log –oneline –graph –decorate –all, use below command.

git config –global alias.history “log –oneline –graph –decorate –all”

Here, history is the alias that you create for “log –oneline –graph –decorate –all” command. So directly use “git history” to see history without that command.

word history is replacement for “log –oneline –graph –decorate –all” command.

So now you can use git history — filename.txt ( any file which is already added to git) to see history of that file.

To see config file details use: git config –global –list

Rename git files:

To rename a file which is updated and pushed to git, use “git mv originalfilename.txt newfilename.txt” and commit the change by git commit -m “renamed file”

To rename git file, use git mv command and then commit it.

Similarly to delete a file from git, use “git rm filename.txt” and then commit it. At any stage, use “git status” to check the current status of your files.

You can also use “rm filename.txt” to delete a file, but again you need to use “git rm filename.txt” and then commit. Because “rm filename.txt” is not git command. So, we have to use git to remove file and then commit.

Never use non-git commands in git repository because git doesnt recognize those commands, and you need to execute the same commands using git again. Ex, if you remove file with rm filename.txt you have to remove the file in git using “git rm filename.txt” and commit. Instead, use git rm filename.txt and commit.  If you have used non-git commands, use “git add -A” command for git to recognize all the changes you made. Next, commit your changes, git commit -m “bla bla bla”

when you used non-git command to add or rename files, use git add -A. Ex, mv filename.txt newfilename.txt (filename.txt is already in git repo) and then commit.

When you delete file without git and later you want git to recognize it, use git add -u and then commit.

Exclude or ignore specific files in git repo:

When you create a file in git repo folder, git would see the file as not added to git, not committed etc.. But if you want to exclude specific files or specific file extensions, create a .gitignore file and add file names in it or file extensions like *.txt, *.log etc. Either individual files or all the files with the extension you mentioned, will be ignored by git.

Ex, notepad .gitignore – would create an empty .gitignore file.

type *.txt or *.log or any specific file name to be ignored by git.

HEAD in Git:

Head is last commit of current branch. Head location can be changed manually if needed, to other commits(not the last commit).  The HEAD in Git is the pointer to the current branch reference, which is in turn a pointer to the last commit you made or the last commit that was checked out into your working directory. That also means it will be the parent of the next commit you do. It’s generally simplest to think of it as HEAD is the snapshot of your last commit.

The HEAD is a special pointer that simply points to the currently checked out branch or commit. And again, it’s a simple file inside the .git folder called HEAD which in our case currently contains the string master.

DIFF in GIT:

using “diff” command, we can get the difference between two commits or any one commit and HEAD.

use “git log –oneline –graph –decorate –all” to see all commits. note the commit id for any file you want to get the differences for.

use “git diff COMMITID FILENAME”

Example, “git diff ca33992 README.md” This command will display differences between commit id, ca33992 and README.md file.

Note: ca33992 is a commit on README.md file.

To use GUI mode for diff, use git difftool command as below:

git difftool 7711148 README.md

This will open default difftool. P4merge is good difference tool.

Use “git diff” command to see differences between recently changed in working directory and HEAD(last commit on the branch)

we can use “git difftool” for GUI.

Branch:

Branch is timeline of commits. They are names that we give to commits.

A branch in Git is simply a lightweight movable pointer to one of these commits. The default branch name in Git is master. As you initially make commits, you’re given a master branch that points to the last commit you made. Every time you commit, it moves forward automatically.

Merging:

Fast forwarding  – This happens when there are no commits(or no additional work is detected) on master branch. Git will apply all commits from other branch to parent branch(master). We can disable this merge if not needed.

Automatic- This happens when there are no conflicting changes between other branch and master branch(parent branch). Git would automatically resolve if it detects any conflicts and a new merge commit is created when conflicts are resolved. So that we know a conflict happened and git resolved it.

Manual – when git is unable to resolve any conflicts manual merge should take place. Git creates a conflicting merge commit state when it is not able to resolve any merge commits. All merge conflicts should be resolved before we commit. Once all conflicts are resolved, then a new commit is created.

Create New Branch:

Use “Git branch” command to see the list of all branches.  The one in green and * symbol represents the current branch.

Note: you need to commit atleast one file to use git branch command.

To create new branch, use “git checkout -b NEWBRANCHNAME” ex, “git checkout -b branch1”

To switch branches use “git checkout OTHERBRANCHNAME”

Move to new branch and modify a file. Add and commit it. Now move to old branch and use git diff newbranch oldbranch so that you can see the differences.

Use git log –oneline –graph –decorate –all to see the commit history. We are using –all parameter here, so this command will show commits from all branches. Now you will see commits from above branch too. To merge branches, use “git merge OTHERBRANCHNAME“. Ex, if you are in master branch and you have another branch name branch1, use “git merge branch1” to merge all the commits from branch1 to master.  You will see what merging method is used, ie., fast-forward or automatic or manual.

To delete a branch “git branch -d OTHERBRANCHNAME” to delete branch. You have to move out of the branch that you want to delete. If you are in that branch, and try to delete it, it wont delete.

Resolving conflicts:

When you end up in any conflicts in your branches,  use mergetool and save any of the files shown there. Once you save, commit your changes.

Tagging:

You can think of a branch as one of those sticky bookmarks:

You can think of tags as chapter headings.

Create tag, “git tag TAGNAME” ex, git tag mytag.

List tag, “git tag –list”  to show all the tags.

Delete tag, “git tag -d TAGNAME” ex, git tag -d mytag to delete the tag, mytag.

To delete all local tags at once, ” git tag | xargs git tag -d“.

Git annotated tags:

Unless you select the -s flag for a signed tag, the tag will be a lightweight, unsigned tag by default. There are also some differences with lightweight tags:

  • When you use git tag <tagname>, Git will create a tag at the current revision but will not prompt you for an annotation. It will be tagged without a message.
  • When you use git tag -a <tagname>, Git will prompt you for an annotation unless you have also used the -m flag to provide a message.
  • When you use git tag -a -m <msg> <tagname>, Git will tag the commit and annotate it with the provided message.
  • When you use git tag -m <msg> <tagname>, Git will behave as if you passed the -a flag for annotation and use the provided message.

Basically, it just amounts to whether you want the lightweight tag to have an annotation associated with it or not.

The big difference is perfectly explained here. Basically, lightweight tags are just pointers to specific commits. No further information is saved; on the other hand, annotated tags are regular objects, which have an author and a date and can be referred because they have their own SHA key. If knowing who tagged what and when is relevant for you, then use annotated tags. If you just want to tag a specific point in your development, no matter who and when did that, then lightweight tags are good enough. Normally you’d go for annotated tags, but it is really up to the Git master of the project.

From <https://stackoverflow.com/questions/11514075/what-is-the-difference-between-an-annotated-and-unannotated-tag>

Git Stashing:

Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the git stashcommand.

Stashing takes the dirty state(or current modified state) of your working directory — that is, your modified tracked files and staged changes — and saves it on a stack of unfinished changes that you can reapply at any time.

From <https://git-scm.com/book/en/v1/Git-Tools-Stashing>

How to Stash:

When you have modified one or more files in current branch, type “git stash” to save the current state of your branch files. To view all stashed files, use “git stash list“. Now if you use “git status” there wont be anything, normally when you edit a file and use “git status“, it should show the modified file in red color. But now you don’t see anything as current state is saved.

NOTE: Stashing only works with files that are modified WHICH ARE ALREADY CREATEAD AND COMMITTED IN GIT. If you add new file and try to stash it, it doesn’t work. You should have a file in git, already created and committed. If you work on these files, you can stash them. NOT ON NEW FILES.

To continue working on the statsh again, use “git stash pop“. This will let us continue working on the files we saved earlier. Now you can edit the file again, add the file to your git and commit and so on.

Git status – to check current state.

Git stash – to save current state, now use “git status” you don’t see your modifiled files. They are in saved state now.

You have modified a file already in git, and used git stash. Now it is in saved state. You modify another file in git, and again use git stash. Now when you say git stash list, you will see multiple stashes.

Git stash list – see all the saved sessions/stashes on all branches.

Git stash pop – to come out of the stash and continue working on the files again. Remove a single stashed state from the stash list and apply it on top of the current working tree state.

Git stash pop stash@{0} and git stash pop stash@{1} and so on to pop your individual stashes when you have multiple stashes.

Note: your new stashes will be saved from the beginning of stash list. Ex, You modified file1 in git, and stashed it. It will be in stash@{0}. Now you modified file2 in git and if you stash it, it will be stash@{0} and the one you saved earlier for file1 would be in stash@{1}. All new stashes will be added to the top of the list. Now if you want to work on file2, you should say git stash pop stash@{0}. If you want to work on file1, you should say git stash pop stash@{1}.

Now you can edit the file again, add it to git and commit.

For a single file edits and commits, you can use the following commands:

  • git config –global user.name “username”
  • git config –global user.email “user email”
  • git clone https://remoterepository.com/username/projectname.git
    Note: By default when you clone, git will create same folder as the one you have in your remote repository( ie., a folder with your project name in your local machine, in your current folder). If  you dont want it, and you need a different folder name, use git clone https://remoterepository.com/username/projectname.git foldername
  • git add filename.ps1
  • git commit -m “Adding filename”
  • git push

This will clone your existing repository, add a file, commit it and push it to your remote repository.

Remove remote repository:

To remove remote repository use: Git remote rm origin

Note: git remote rm does not delete the remote repository from the server. It simply removes the remote and its references from your local repository. git remote rm origin removes the connection to the remote server. git remote add origin <user>@<url> will recreate this connection.

Differences between fetch and push:

In a general scenario, when you have changes only on your local repo, you can use git push to send all your local changes to your remote repo. But when you modify a file on your remote repo, like on github via browser, your local repo cannot identify that change.

So you must first fetch your remote repo changes first(note, this doesn’t merge anything, we are just acknowledging that there are changes on remote repo) and then use git pull to pull the online(remote) repo changes to your local repo. Now you can make changes to the same file in your local repo, and then use git push to push the changes.

Use git fetch origin master to fetch all the files from your master branch.

Git fetch would only pull references from your online repo. It doesnt merge any files or copy any changes you made to files in your online repository.

This would be very useful when multiple members are working on the same project or code. You import/fetch all the code written by your team, and you start working on it.

If you have modified files on online repo, ie., browser and in local repo, when you try to push, it says “updates are rejected because remote contains work that you do not have locally.”

Now you need to fetch and pull. If both files are different, you fetch them, pull them , and push your local repo.

But if same file is modified on browser(online repo) and in your local repo, When you try to push,

  • If you dont commit your local repo file changes, it will throw an error as below:
    • * branch master -> FETCH_HEAD
      Updating 216d42f..91a8fef
      error: Your local changes to the following files would be overwritten by merge:
      another file on local repo.txt
      Please commit your changes or stash them before you can merge.
      Aborting
  • If you commit and then try to fetch and pull, it says this:
    • * branch master -> FETCH_HEAD
      Auto-merging another file on local repo.txt
      CONFLICT (content): Merge conflict in another file on local repo.txt
      Automatic merge failed; fix conflicts and then commit the result.

So, you need to remove your changes locally, then have a clean fetch and pull, and then edit your local file, and push.

Some important points:

  • When you have modified different files on remote repo and local repo(one file on online repo and a different file on your local repo, not same file at both places), you have to first fetch and pull on local repo, and then push your locally modified file to online repo.
  • If you have same file modified on online repo and local repo, it throws an error as shown above. You have to remove changes locally and fetch, pull and then push.

Check Git Proxy settings and reset if needed:

git config –global -l to see the git configuration in your system, like default name, email id, git version, proxy etc..

git config –global –unset http.proxy to reset proxy specification in your system.

Commands to reset git on http, https and core proxy:

git config –global –unset http.proxy
git config –global –unset https.proxy
git config –global –unset core.gitproxy

Fork in GitHUB or GitLAB:

A fork is a copy of a repository. Forking a repository allows you to freely experiment with changes without affecting the original project.

Copying existing repository from someone else’s GitHub account to your GitHub account and working on it is called Forking. You can work on your forked copy and it doesn’t affect or change the original repository that you copied from, because both repositories are in different GitHub accounts.

Most commonly, forks are used to either propose changes to someone else’s project or to use someone else’s project as a starting point for your own idea.

Propose changes to someone else’s project

A great example of using forks to propose changes is for bug fixes. Rather than logging an issue for a bug you’ve found, you can:

  • Fork the repository.
  • Make the fix.
  • Submit a pull request to the project owner.

If the project owner likes your work, they might pull your fix into the original repository!

Use someone else’s project as a starting point for your own idea.

At the heart of open source is the idea that by sharing code, we can make better, more reliable software.

When creating your public repository from a fork of someone’s project, make sure to include a license file that determines how you want your project to be shared with others.

For more information on open source, specifically how to create and grow an open source project, we’ve created Open Source Guides that will help you foster a healthy open source community by recommending best practices for creating and maintaining repositories for your open source project. You can also take a free GitHub Learning Lab course on maintaining open source communities.

Pull Request:

From Github’s Using Pull Requests Page

Pull requests let you tell others about changes you’ve pushed to a GitHub repository. Once a pull request is sent, interested parties can review the set of changes, discuss potential modifications, and even push follow-up commits if necessary.

Pull Requests are commonly used by teams and organizations collaborating using the Shared Repository Model, where everyone shares a single repository and topic branches are used to develop features and isolate changes.

So, other team members fork your project, update their own code in that repo, and send a pull request. Meaning, they have a piece of code that works and they send you(admin or owner) a request to pull that code into your repo. Now, as admin, you can accept it and merge it in your repo or reject that pull request.

When you(not the owner or the project, but someone else) open a pull request, you’re proposing your changes and requesting that someone(typically owner or admin of the original repo) review and pull in your contribution and merge them into their branch.  Pull requests show diffs, or differences, of the content from both branches. The changes, additions, and subtractions are shown in green and red.

Learn Git:

  • Git Immersion : A guided tour to teach you the basics of Git.
  • Try Git : An interactive series of challenges to learn about and experiment with Git.
  • GitHub Learning Lab : Setup github training in your repository and run labs. Very useful.
  • GitGuys