text:
# CoreCLR's environment is not your environment

CoreCLR maintains its own private copy of environment variables

This came up when I was helping another collegue in a previous job (where I still write C# code probably 50% of the time), diagnosing a library load failure problem inside a linux container. Internally there is this library that loads different implementations (mock implementation and real implementation) of another library based on a environment variable USE_MAGIC_TEST_LIB, and the .NET code calling that library is calling SetEnvironmentVariable to set it conditionally as part of a testing framework:

if (useTestFramework)
{
Environment.SetEnvironmentVariable("USE_MAGIC_TEST_LIB", "1");
}

This looks reasonable except that it didn’t work at all. It loaded the wrong library and things quickly went down hill after that.

We were scratching our heads for a while until we decided to add a trace to see if the environment is actually taking effect or not in the native code. Interestingly, the native code didn’t see it at all. It’s like they don’t know about each other’s environment!

Actually that observation is more or less what’s going on. Before we dig in a bit deeper, here is a bit of history of CoreCLR cross-platform implementation. Not surprisingly, .NET code started as Windows centric and all the OS calls are strictly Windows API. At some point folks decide to port it to linux/Mac as part of Rotor (later Silverlight), there are two options:

1. Design a new platform abstraction from scratch and move it to that
2. Align the API design to Windows and implement Windows API on top of Linux API

2 is obviously the cheaper solution and has the advantage that Windows code would be untouched and therefore won’t get regressions, which is super important. The caveat is that implementing Windows API using Linux API can get tricky, but is the risk people are willing to take. So the new PAL layer is introduced with “new” APIs that looks exactly like Windows APIs implemented using Linux APIs.

In the case of SetEnvironmentVariable, it is implemented in PAL/environ.cpp:

BOOL
PALAPI
SetEnvironmentVariableA(
IN LPCSTR lpName,
IN LPCSTR lpValue)
{
// ...

// All the conditions are met. Set the variable.
int iLen = strlen(lpName) + strlen(lpValue) + 2;
LPSTR string = (LPSTR) PAL_malloc(iLen);
if (string == nullptr)
{
bRet = FALSE;
ERROR("Unable to allocate memory\n");
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto done;
}

sprintf_s(string, iLen, "%s=%s", lpName, lpValue);
nResult = EnvironPutenv(string, FALSE) ? 0 : -1;

PAL_free(string);
string = nullptr;

// If EnvironPutenv returns FALSE, it almost certainly failed to allocate memory.
if (nResult == -1)
{
bRet = FALSE;
ERROR("Unable to allocate memory\n");
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
goto done;
}

This looks a bit fishy. It’s allocating its own buffer and calls into EnvironPutenv, which basically does this:

    for (i = 0; palEnvironment[i] != nullptr; i++)
{
const char *existingEquals = strchr(palEnvironment[i], '=');
if (existingEquals - palEnvironment[i] == nameLength)
{
if (memcmp(entry, palEnvironment[i], nameLength) == 0)
{
free(palEnvironment[i]);
palEnvironment[i] = copy;

result = TRUE;
break;
}
}
}

if (palEnvironment[i] == nullptr)
{
_ASSERTE(i < palEnvironmentCapacity);
if (i == (palEnvironmentCapacity - 1))
{
// We found the first null, but it's the last element in our environment
// block. We need more space in our environment, so let's double its size.
int resizeRet = ResizeEnvironment(palEnvironmentCapacity * 2);
if (resizeRet != TRUE)
{
free(copy);
goto done;
}
}

_ASSERTE(copy != nullptr);
palEnvironment[i] = copy;
palEnvironment[i + 1] = nullptr;
palEnvironmentCount++;

result = TRUE;
}

So it’s basically managing its own memory in palEnvironment environment array! No wonder things don’t work.

But why go through all the trouble while Linux has its own getenv/setenv?

These posts provides the answer:

http://rachelbythebay.com/w/2017/01/30/env/

Modifications of environment variables are not allowed in multi-threaded programs. – the glibc manual

https://github.com/dotnet/coreclr/issues/635

From looking at the code, I suspect that the cached environment was attempt to fix thread safety or consistency problems between Environment.GetEnvironmentVariables and Environment.SetEnvironmentVariable.

The enumeration of the environment starts by reading the environ global variable, without any locks. Consider what may happen if somebody calls setenv while the enumeration is in progress.

It’s because setenv/getenv isn’t particularly thread safe - you can crash when reading environment while the environment get modifed by another thread, or two threads modifying environment at the same time can lead to leaks.

In this case, one can see a few options:

1. Do nothing - the issues are linux-specific and you should take care when calling these functions, the same way just like you call them in linux.
2. Throw PlatformNotSupported - getenv/setenv just isn’t safe
3. Adding critical section around getenv/setenv - make them safe to be called in multiple threads
4. Implement your own safe environment helpers - as a result rest of the native library won’t observe the change through getenv/setenv

1 isn’t really acceptable because .NET developers need the code to be portable - they don’t want handle the platform special oddies to make their code portable. They would like .NET platform library to be safe and reliable.

2 isn’t great either for the same reason, and also it’ll break a ton of code when ported to linux.

3 makes .NET code safe, but it wouldn’t protect against native code racing with getenv/setenv calls from within .NET code, so race conditions would still occur and .NET developer has little control.

4 is safe, but can lead to subtle breaking changes.

Unfortunately there isn’t a great option here. 1, 2, and 4 are safe option, but all of them have their downsides. At the end of the day, it comes down to compatibility/portability vs surprising behavior. .NET team favors compatibility and portability. While it can lead to sutble breaking changes, fortunately the breaking changes are consistent and therefore easier to diagnose. In our case being a container launched by another system makes the whole thing much harder to diagnose, but that’s more a problem of the container launcher itself. Even though we were bit by the very same problem, I agree it is most likely the better choice.

For more information, you can refer to CoreCLR code here:

https://github.com/dotnet/coreclr/blob/master/src/pal/src/misc/environ.cpp#L607

And here is a simple code snippet to demonstrate the issue. I’ve tested this on my MBP.

using System;
using System.Runtime.InteropServices;

namespace set_env
{
class Program
{
[DllImport("/usr/lib/system/libsystem_c.dylib")]
static extern IntPtr getenv(string name);

[DllImport("/usr/lib/system/libsystem_c.dylib")]
static extern int setenv(string name, string value);

static void Main(string[] args)
{
string envName = "MY_ENV";

Console.WriteLine("MY_ENV={0}", Environment.GetEnvironmentVariable(envName));

Environment.SetEnvironmentVariable(envName, "~/path");
Console.WriteLine("Setting it to ~/path");

Console.WriteLine("MY_ENV={0}", Environment.GetEnvironmentVariable(envName));

IntPtr env = getenv(envName);
string envStr = Marshal.PtrToStringAnsi(env);
Console.WriteLine("getenv(MY_ENV)={0}", envStr);

Console.WriteLine("Setting it using setenv");
setenv(envName, "~/path");

env = getenv(envName);
envStr = Marshal.PtrToStringAnsi(env);
Console.WriteLine("getenv(MY_ENV)={0}", envStr);
}
}
}

Now if you are interested to dig in a bit more, here are some bonus questions for your consideration:

1. Does the same problem happen in Windows? And why/why not?
2. Why does the above code above use Marshal.PtrToStringAnsi instead of just have getenv returning the string?

That’s all for now. Thanks for reading!


text:
# Math Help - differentiation

1. ## differentiation

Hello everybody,

I am new on this forum and I really really need your help hope that i did post my problem in the good forum.

I am doing a master in transport and I have this exercice to do:

one a one-way carriageway, the relation between space mean speed Vs (km/h) and density k (veh/km) is: Vs=v0exp(-k/km), where v0=100km/h.

The relation between flow q and density k is q=kv for heterogeneous traffic and q=k.Vs for heterogeneous traffic, so q=k.v0exp(-k/km).

I have to find by differentiation k but I am lost. I tried everything but cannot find k. k is suppose to be equal to 50.

Could you help or guide me a bit.

Thanks

2. It is not entirely clear what you wish to do.

I don't know the physics behind the question in detail, but I know how to differentiate. When you say that you want to find k by differentiation, I suppose you want to find a value of k that makes some function maximal or minimal? And a second question, exactly which of the given functions do you wish to differentiate?

3. ## differentiation

I want to differentiate all that equation with the values I gave above:
q=k.v0exp(-k/km). v0= 100 and km=50.

Hope it is clear now.

4. To differentiate with respect to k then use the product rule with $u=kv_0$ and $v = e^{-k/k_m}$ (chain rule)

format: plain


text:
125 views

### How to get details about how Mathematica did a definite Integral

Assuming[{Element[S, Reals],S>0},Integrate[Exp[-I*S*w]/(w^2 + 1)^(3/2) ,{w, 0, Infinity}]] gets ...
121 views

### Can Wolfram Development platform use wolfram alpha?

I would like to be able to acces/show the step by step solutions from wolfram alpha in a note book! Is this possible? An
218 views

### functional derivative a functional

Can we calculate step by step functional derivative of the following functional by Mathematica? $$J(y) = \int_{x_0}^{x_1} \left( y^6(x) \right) \, \mathrm{d}x$$
334 views

### Equivalent of Maple ShowSolution command in *Matematica* [duplicate]

While responding to a comment (...solve it by hand quickly) on this question, I used Maple to see all the steps in an integration, differentiation, and limit, like this The Maple command ...
221 views

1k views

### How to show steps of Integrate[Exp[-(I*(s*t))]/(Sqrt[2*Pi]*Sqrt[s]), {s, 0, Infinity}]? [duplicate]

I am trying to use Wolfram Alpha in Mathematica 10.2 to show me the steps for calculating the integral in the title, i.e I have written the following command: show steps of ...
36 views

### Can we find a way to see how Mathematica does calculation? [duplicate]

Sometimes when we have an expression to calculate, it is the kind of calculation that we do not know how to calculate by ourselves. We use Mathematica to calculate the expression, and Mathematica ...
46 views

### How to print intermediate steps of simplifying a power formula? [duplicate]

To answer the question of proving Fibonacci sequence is periodic mod 5 without using induction., I came across Mathematica to prove $$F_{n}\equiv F_{n+20}\pmod 5$$ for all $n \geq 2$ I defined: \$F[n]...
544 views

### Mathematica's steps towards the result of a definite integral

I am interested in this definite integral: \eqalign{\int_0^\infty {x^2 \log(1-e^{-x})}\; dx\cr } That is: ...


text:
# What is the sequence type if ROC is an annulus in the z-plane

if:

$$|z|>|\alpha|$$ is causal and right-sided

and:

$$|z|<|\alpha|$$ is anti-causal and left-sided

then, what is the annulus/donut-ring ROC?

$$|\beta| < |z| < |\alpha|$$

non-casual? finite-length? double-sided?


text:
Greg Foletta - Bits and Blobs

A melange of networking, data science and programming.

Git Under the Hood

While I’m not a programmer per se, I do use git almost daily and find it a great tool for source control and versioning of plain text files. But I don’t think there can be any doubt that it is not the easiest tool to use. But despite its unintuitive user interface, under the hood git is quite simple and elegant. I believe that if you can understand the fundamental constructs git uses to store, track, and manage files, then the using git becomes a lot easier.

In this article we’re going to take a look under the covers and investigate git’s fundamental constructs. We’ll start off with its storage model and look at blobs, trees and commits. We’ll see how branches are implemented, and finally we’ll unpack the git index file to understand what happens during the staging of a commit.

1. We’re going to avoid the lower level ‘plumbing’ git commands and limit ourselves to the five most common ‘porcelain’ commands: git {init, add, commit, branch, checkout}. All other work will be done using standard command line utilities.
2. Using the R packages git2r and tidygraph, we’ll dynamically build up a picture of the connections between git’s objects to help understand how they are tied together.

Initialisation

We’ll start by initialising a git repository, which creates a .git directory and some initial files. Git holds all of the files and metadata it needs for source control in this directory. To clarify things we’ll prune back as many of the initial files as possible, while still ensuring git recognises it as a valid repository.

# Initialise the repository (quietly)
git init --quiet

# Remove some of the created files and directories
rm -rf .git/{hooks,info,config,branches,description}
rm -rf .git/objects/{info,pack}

tree .git

.git
├── objects
└── refs

2 directories, 1 file

With a nice clean slate we can move on to our first object: the blob.

Blobs

The first fundamental git object we’ll look at is the blob. If we create a file and add it to the staging area, we see what has changed in the .git directory.

# Create a file
echo "Root" > file_x

# Add it to the staging area

tree .git

.git
├── index
├── objects
│   └── 93
│       └── 39e13010d12194986b13e3a777ae5ec4f7c8a6
└── refs

3 directories, 3 files

There’s two new files - an index and an object. We’ll get to the index later in the article, but for now let’s focus on the object. Checking it’s format we find that it’s compressed data:

file .git/objects/93/39e13010d12194986b13e3a777ae5ec4f7c8a6

.git/objects/93/39e13010d12194986b13e3a777ae5ec4f7c8a6: zlib compressed data

Decompressing and looking inside, we see the file is in a structured as a “type, length, value” or TLV. The type is ‘blob’, the length is the size of the of the data (excluding the type), and the value the contents of the file we created:

pigz -cd .git/objects/93/39e13010d12194986b13e3a777ae5ec4f7c8a6 | hexdump -C

00000000  62 6c 6f 62 20 35 00 52  6f 6f 74 0a              |blob 5.Root.|
0000000c

How is the path to the blob object created? It’s simply the SHA1 hash of the blob itself. The first two hexadecimal digits are used as a folder, with the remaining digits used as the object file name. We can show this by recreating the object, taking the hash, and noting it’s the same as our object’s path:

# The hash matches the object's path
echo "blob 5\0Root" | shasum

9339e13010d12194986b13e3a777ae5ec4f7c8a6  -

It can therefore be helpful to understand git as a form of content addressable storage: the location of the data that is under its control is based on the content of the data itself. Let’s explore this further: we’ll add two files with the same contents, one in the root directory and one in a subdirectory.

# Cerate a subdir
mkdir subdir

# Add two files, one in the root, one in the subdir
echo "Root & Sub" > file_y
echo "Root & Sub" > subdir/file_z

# Add the files to the staging area

tree .git

.git
├── index
├── objects
│   ├── 93
│   │   └── 39e13010d12194986b13e3a777ae5ec4f7c8a6
│   └── cc
│       └── 23f67bb60997d9628f4fd1e9e84f92fd49780e
└── refs

4 directories, 4 files

While we’ve added three files but there’s only two objects. Blobs only contain raw data, with no reference to files or directories. There’s only two pieces of unique data, and thus two objects. The file/directory information is stored in tree objects which we will take a look at in the next section.

Over the course of the article we’re going to build up a graph of the objects in the git repository to gain a visual representation of the structure. Here’s our starting point: two blobs, the first four characters of their hash, and their contents:

The two little blobs of data look pretty lonely out there. What they need is some context, and this is where the tree object comes in.

Tree

As we’ve seen, there’s no file or directory information in the blob object. The storage of this information is the role of the tree object. Let’s perform our first commit and see what’s changed in the repository:

git commit --quiet -m "First Commit"

tree .git

.git
├── COMMIT_EDITMSG
├── index
├── logs
│   └── refs
│           └── master
├── objects
│   ├── 36
│   │   └── 58bfd8a7cda8ee50181497ab8ec4e699428877
│   ├── 4e
│   │   └── eafbc980bb5cc210392fa9712eeca32ded0f7d
│   ├── 67
│   │   └── 21ae08f27ae139ec833f8ab14e3361c38d07bd
│   ├── 93
│   │   └── 39e13010d12194986b13e3a777ae5ec4f7c8a6
│   └── cc
│       └── 23f67bb60997d9628f4fd1e9e84f92fd49780e
└── refs
└── master

11 directories, 11 files

A lot has changed and at first glance it may be a bit overwhelming, but focus in on the objects subdirectory. There’s an additional three objects on top of the two blob objects we saw before. What are they? If you’ll forgive me, I’ll use a very ugly set of commands to pull out the type and length from each of the objects:

# May god have mercy on my soul
find .git/objects -type f -exec sh -c \
"echo -n '{} -> ' && pigz -cd {} | perl -0777 -nE 'say unpack qw(Z*)'" \;

.git/objects/cc/23f67bb60997d9628f4fd1e9e84f92fd49780e -> blob 11
.git/objects/36/58bfd8a7cda8ee50181497ab8ec4e699428877 -> commit 175
.git/objects/4e/eafbc980bb5cc210392fa9712eeca32ded0f7d -> tree 101
.git/objects/67/21ae08f27ae139ec833f8ab14e3361c38d07bd -> tree 34
.git/objects/93/39e13010d12194986b13e3a777ae5ec4f7c8a6 -> blob 5

So in addition to our two blobs, we’ve got two trees and a commit. Our starting point for will be the first of the tree objects. Unlike the others, trees contain some binary information rather than UTF-8 strings. I’ll use Perl’s unpack() function so decode this into hexadecimal:

pigz -cd .git/objects/4e/eafbc980bb5cc210392fa9712eeca32ded0f7d |\
perl -nE 'print join "\n", unpack("Z*(Z*H40)*")'

tree 101
100644 file_x
9339e13010d12194986b13e3a777ae5ec4f7c8a6
100644 file_y
cc23f67bb60997d9628f4fd1e9e84f92fd49780e
40000 subdir
6721ae08f27ae139ec833f8ab14e3361c38d07bd

Like the blob we have the type and length of the object. Following this there’s an entry for each of the two files and the subdirectory that reside in the root directory. The digits preceding the file name are the mode, capturing the type of filesystem object (regular file/symbolic link/directory) and its permissions. Git doesn’t track all combinations of permissions, only whether the file is user executable or not.

Each of the the two file entries point to the hashes of the blob objects, but the subdirectory points to the hash of another tree object. Looking inside that tree object:

pigz -cd .git/objects/67/21ae08f27ae139ec833f8ab14e3361c38d07bd |\
perl -nE 'print join "\n", unpack("Z*(Z*H40)*")'

tree 34
100644 file_z
cc23f67bb60997d9628f4fd1e9e84f92fd49780e

We see this has an entry for the file_z in the subdirectory, and this points to the same hash as the file_y entry in the previous tree. A graph should make this clearer:

At the top is the root of the tree, with vertices to the two blobs (file_x and file_y) and a tree (the subdirectory). The second tree is has a single vertex to a blob (file_z). The root and subdirectory trees both point to the same blob, because both files have the same contents.

With blobs and trees we’ve built up a pseudo-filesystem, but how does this help us with source control? The object that ties all of this together is the commit.

Commit

Our third object - and the one most visible to the end user - is the commit. We’re going to open our commit up, but as we’ll learn about shortly, the hash of the commit (and thus the path to the object) depends partially on the time the commit was made. As I generate this article dynamically, there’s a little bit of overhead to get the commit object:

# Get the commit object
COMMIT_DIR=$(cut -c 1-2 .git/refs/heads/master) COMMIT_OBJ=$(cut -c 3- .git/refs/heads/master)

# Open it up
pigz -cd .git/objects/$COMMIT_DIR/$COMMIT_OBJ |\
perl -0777 -nE 'print join "\n", unpack("Z*A*")'

commit 175
tree 4eeafbc980bb5cc210392fa9712eeca32ded0f7d
author Greg Foletta <greg@foletta.org> 1654027280 +1000
committer Greg Foletta <greg@foletta.org> 1654027280 +1000

First Commit

Again we have the type and length of the object at the start, then there’s a few different pieces of information.

The first is reference to a tree object. This is the tree object that represents the root directory of the repository. The next is the author of the commit, with their name, email address, commit time and the UTC offset. As the person who authored the commit doesn’t necessarily have to be the person who committed it to the repository, there’s also a line for the committer. Following this is the commit message, which is free text input entered at the time of the commit.

Let’s place this on our graph:

The commit points to the tree object representing the root directory. But this first commit is a special commit, missing a piece of information: parents. Every other commit in this repository from this point onwards will point to one or more parent entries. This creates a directed acyclic graph (DAG), allowing any commit to be traced back to the first commit. As every commit’s hash is dependent on its parents’ hash, the graph is also a form of Merkle tree.

If we change the contents of file_z, stage, and create a second commit, will allow us to see this additional information in the commit object:

# Change the contents, add, and create a second commit
echo "Root Changed" > file_x
git commit -q -m "Second Commit"

# Determine the path to the second commit object
COMMIT_DIR=$(cut -c 1-2 .git/refs/heads/master) COMMIT_OBJ=$(cut -c 3- .git/refs/heads/master)

# Unpack the contents of the commit
pigz -cd .git/objects/$COMMIT_DIR/$COMMIT_OBJ |
perl -0777 -nE 'print join "\n", unpack("Z*A*")'

commit 224
tree 6e09d0dbb13d342d66580c40a49dd1583958ccc8
parent 3658bfd8a7cda8ee50181497ab8ec4e699428877
author Greg Foletta <greg@foletta.org> 1654027282 +1000
committer Greg Foletta <greg@foletta.org> 1654027282 +1000

Second Commit

We can then place the second commit onto our graph, omitting the link back to the parent (we’ll get to that in the next section):

What we see here represents the core of how git stores data. The commits represent ‘snapshots’ of the state of the files and directories in the repository. In this particular scenario commits point to different root tree objects, each of which point to different objects representing the file_x. But they share the same tree object representing the subdirectory and the blob representing file_y.

There’s no ‘diffs’ calculated between commits; if one byte changes in a file, a new blob is created, resulting in a new tree (or trees), resulting in a new commit. In terms of the space-time trade-off, git chooses space over time, resulting in a simple model of data changes over time.

The problem is, our commits are still addressed via a 160 bit hash. This is all well and good for a computer, but we’d like something a bit more human friendly. This is the role of branches.

Branches are relatively simple: they are a human-friendly, named pointer to a commit hash. Local branches (as opposed to remote branches, which we won’t cover in this article) are stored in .git/refs/heads/, and right now the current master1 branch points to the hash of our second commit:

cat .git/refs/heads/master

89ec2b06b21f25cdbd763924c751c8b24886d5c2

We also need to briefly mention HEAD. This file tracks which commit is currently ‘active’, i.e. the checked out files match those in the commit. We see HEAD currently refers to our master branch2:

cat .git/HEAD

ref: refs/heads/master

If we create a new branch, it will recurse on what HEAD points to until it finds a commit:

# Create a new branch
git branch branch_2

# List the branches and the hashes they point to
find .git/refs/heads/* -type f -exec sh -c 'echo -n "{} -> " && cat {}' \;

.git/refs/heads/branch_2 -> 89ec2b06b21f25cdbd763924c751c8b24886d5c2

When a new commit is issued, the current branch is moved to point to the new commit (and head will indirectly point to the commit through this branch):

# Checkout a branch and commit on it
git checkout -q branch_2
echo $RANDOM > file_x git commit -q -am "Third Commit (branch_2)" # The 'new_branch' branch now points to a different commit. find .git/refs/heads/* -type f -exec sh -c 'echo -n "{} -> " && cat {}' \;  .git/refs/heads/branch_2 -> 54d46328e009399d656f158c04df8ad9c2b24cf6 .git/refs/heads/master -> 89ec2b06b21f25cdbd763924c751c8b24886d5c2  If we checkout the master branch and creating a new commit, we ca visualise how the two branches have diverged: # Commit back on the master branch git checkout -q master echo$RANDOM > file_x
git commit -q -am "Fourth Commit (master)"

The third and fourth commits are both descendants of the third commit. Adding in our branches to the graph we see they point to the tip of this graph:

There’s nothing stopping us from creating more branches, even if they point to the same location:

# Create new branches
git branch branch_3
git branch branch_4

The key takeaway is that branches provide a human-friendly way of navigating the git’s commit DAG.

Index

The final file we’re going to look at is the index. While it has a few purposes, we’ll focus on it’s main role as the ‘staging area’. The exact structure is available here, and I’ve converted this into Perl unpack() language:

perl -0777 -nE '
# Extract out each file in the index
my @index = unpack("A4 H8 N (N4 N2 n B16 N N N H40 B16 Z*)");

say "Index Header: " . join " ", @index[0..2];
say "lstat() info: " . join " ", @index[3..12];
say "Object & Filepath: " . join " ", @index[13..16];
' .git/index

Index Header: DIRC 00000002 3
lstat() info: 1654027283 118849971 1654027283 118849971 64769 7734889 0 1000000110100100 1000 1000
Object & Filepath: 6 511c5ae2b662376b23658fe922231d824d4e03e6 0000000000000110 file_x

The first line shows the the four byte ‘DIRC’ signature (which stands for ‘directory cache’), the version number, and the number of entries (files in the index). We’ve only unpacked one of the files.

The first fields contain information from the lstat(2) function: last changed and modified time, the device and inode, permissions, uid and gid, and file size. These values allow git to quickly determine if files in the working tree have been modified.

Then comes the hash of the object, a flags field (which includes the length of path), and the path to the object.

If we recall back in the blobs section, when we added a file to the staging are via git add, the index was created. Let’s modify file_x and add it to the staging area:

echo "Index Modification" > file_x

Now we’ll re-take a look at the index:

ctime, mtime: 1654027286 1654027286
object, filepath: db12d29ef25db0f954787c6d620f1f6e9ce3c778 file_x

The create and modify times have changed, and so has the object that file_x points to. If a git commit is issued, the tree underlying the commit is based upon the current state of this index. When a different branch is checked out, the index is rebuilt so that the files in index point to the correct blobs for that particular commit.

Conclusion

In this article we dived in to the internals of git. We’ve looked at git’s data model and learned about the raw blobs of data, the trees that hold the filesystem information, and the commits that point the root of the tree. We’ve seen how commits have parents, creating a directed acyclic grapg of differing states of the repository.

Branches were shown to be quite simple, human-readable pointers to commit hashes, allowing us to navigate around git’s commit graph. Finally we cracked open index, which is a staging area for the next commit.

It’s perhaps a sad indictment when you have to understand the internals of something in order to use it. This could be bad design, the inherent complexity of the problem domain, or perhaps a little bit of both. Nevertheless git is a widely used tool that’s incredibly powerful, and investing some time to understand it will surely pay dividends down the line when want to manage configuration, code, or almost any other type of text-based content.

1. The default branch, which is now configurable in git version 2.28 and above ↩

2. HEAD doesn’t doesn’t have to refer to a branch, it can refer to an arbitrary commit. This is known as a ‘detatched HEAD’. ↩


text:
# How to write an if then logical constraint given part of the input related to a decision variable?

I am trying to solve an assignment problem-like from a bi-objective persepctive where I have a marketplace of vendors proposing different machines with different types and specs. The goal is to select a small number of vendors (F1) while meeting the specs of some client machines and maximizing a utility function (F2).

The first decision variable Y[j,i,v] = 1 if machine j from vendor v is candidate for machine client i, 0 otherwise.

The second one is related to the type of client machine defined as Z[i,t] = 1 if machine i is of type t, 0 otherwise.

Since the input regarding this decision variable is known I set it accordingly to the type. For instance, if machine 1 is of type 1 then Z[1,1] = 1 and so on and each machine has only one type so other values for machine 1 are set to be 0 (Z[1,2] = Z[1,3] =0, etc.).

The last decision variable is related to the selection of vendors A[v]=1 if vendor v is selected, 0 otherwise.

The input of the model is the set of machines with desired specs and types the clients want to buy, the vendors with their machines, specs and types. This is stored in different data structures.

Part of the input related to Y[j,i,v] contains the type of machines. Since the type of machines is known, I guess I do not need to include it in the definition of the decsion variable Y to be in the form of Y[j,i,v,t]. I would like to write a constraint saying that if machine client i is of type t then the machine candidate should be of the same type.

With the case of Y[j,i,v,t] it looks like : Z[i,t] <= Y[j,i,v,t].

But with the case of Y[j,i,v] I am not sure how to connect it with Z[i,t] using a constraint knowing that t is part of Z and t is a known value in Y.

Anyone can shed some light on this matter? I am using Python and Gurobi as a solver. I attached the link to my linear program format file and the output provided by gurobi (vendors and gurobi_output).

• I am not sure whether I understand the question completely. Your notions of macines, vendors, types and clients make not really sense without further context. However, formulating a logical implication as linear constraint is straight forward (see my answer below). – ttnick Oct 30 '18 at 22:38

If you have two binary variables X, Y and you want to express the constraint X implies Y via a linear inequality you can use the logical equivalent statement (not X) or Y, which in linear programming translates to: $$(1-X) + Y \geq 1$$ In Gurobi-Code this would be: model.addConstr((1-X) + Y >= 1, name="implication").