Implement Git By Yourself (Operations)

Implement Git series

Ugit supports a lot of basic git operations. All of them are wrapped into the Operations namespace to make it manageable.

This operation supports the init command parameter, which has two steps.

  1. Clear the .ugit folder
  2. Create the HEAD to master branch

This operation is designed to manipulate the branch. For examples

  • Show all branches
  • Create one branch

There is no secret in BranchOperation just read and write ref/heads folder. Each file of them represents the branch. HEAD is pointing to the current branch in the ref: refs/head/<branch-name> format.

It’s same with BranchOperation but this time it focuses on ref/tags folder.

This operation’s responsibility is to add file or directory to the stage index.

This operation is one of the backbone of ugit . It writes and reads tree of content of working directory, index and repo.

Working Tree

Working tree represents the current working directory. The result will be a dictionary which key is relative file path and value is blob’s object id.

Stage Index Tree

It will retrieve and update .ugit/index file.

Repo Tree

We can read the checked-in folder and blob according to the tree object id, vice verse.

This operation has main tree responsibilities.

  1. Create/Get a commit
  2. Show a previous commit according to the commit id.
  3. Show all object ids by given commit ids.

Firstly of all, what’s the commit like?

tree <tree-object-id>
parent <parent1-commit-id>
parent <parent2-commit-id2>

A commit may have zero (first commit)or multiple parents (merge commit).

We can retrieve the parents commit according the parent property. So, it doesn’t have challenge to show all commit by given commit id. We have also to leverage the TreeOperation to get all objects.

As the distribution VSC tool, it’s expected to compare and merge two snapshot s of repo. DiffOperation completes two major jobs

  1. Compare the difference of trees and blob files.
  2. Merge them.

Given that we have two trees: A and B. Both of them are represent by the a dictionary which key is the file relative path and value is the file object id. So it comes to four cases. (NaN means this file object id doesn’t exist)

We leverage diff command when come to compare the two files.

diff --unified --show-c-function --label a/{file-a} --label b/{file-b} {tempfile}

How to merge them? For each file object, just reuse MergeBlob method for diff result from two trees.

diff -DHEAD {headfile} {otherfile}

Since ugit is a distributed tool, it should support sync with remote repo. To simply the implementation, we support fetch and push operation in the same file system.

How does the fetch do?

  • Get all the reference in the ref/ folder, which include branch and tag
  • Create the corresponding reference in the local remote folder.
  • Get all the objects by the commit object id.
  • Write them down if doesn’t exist.

We have to push local reference to remote, like branch or tag . First we have to check whether remote reference is ancestor. If not which means someone has forward the remote repo. This push may be dangerous.

  • Push the missing object in the remote
  • Update the remote reference.

A software developer in Microsoft at Suzhou. Most articles spoken language is Chinese. I will try with English when I’m ready