Щоб розширити відповідь Бена Джексона , що добре, давайте уважно розглянемо вихідне питання. (Див. Його відповідь щодо того, чому турбувати питання типу; це більше про те, що відбувається .)
Я новачок у контролі версій, і я розумію, що "фіксація" - це по суті створення резервної копії під час оновлення нової "поточної" версії того, над чим ви працюєте.
Це не зовсім правильно. Резервні копії та контроль версій, безумовно, пов’язані між собою - наскільки сильно залежить від деяких речей, які певною мірою стосуються думки, - але, безумовно, є певні відмінності, хоча б лише за намірами: Резервні копії, як правило, призначені для аварійного відновлення (машина виходить з ладу, вогонь знищує ціла будівля, включаючи всі носії інформації тощо). Контроль версій, як правило, розроблений для детальнішої взаємодії та пропонує функції, яких резервне копіювання немає. Резервні копії зазвичай зберігаються деякий час, а потім видаляються як "занадто старі": більш важливим є більш свіже резервне копіювання. Зазвичай контроль версій назавжди зберігає кожну встановлену версію.
Що я не розумію, так це те, для чого постановка полягає з практичної точки зору. Постановка - це щось, що існує лише за назвою, чи це служить певній меті? Коли ти вчиняєш, він все одно все зробить, так?
Так і ні. Дизайн Git тут дещо своєрідний. Існують системи контролю версій, які не вимагають окремого етапу переходу. Наприклад, Mercurial, який інакше дуже схожий на Git з точки зору використання, не вимагає окремого hg addкроку, крім самого першого, який представляє абсолютно новий файл. З Mercurial ви використовуєте hgкоманду, яка вибирає певний коміт, потім ви робите свою роботу, потім запускаєте hg commit, і все готово. З Git ви використовуєте git checkout, 1 тоді ви робите свою роботу, потім запускаєте git add, а потім git commit. Чому зайвий git addкрок?
Секрет тут полягає в тому, що Git називає, по-різному, індекс або область проходження , або іноді - рідко в наші дні - кеш . Це все назви для одного і того ж.
Редагувати: Я думаю, що я, можливо, плутаю термінологію. "Поетапний" файл - це те саме, що "відстежуваний" файл?
Ні, але це пов’язано. Відслідковуються файл один , який існує в індексі Git і . Щоб правильно зрозуміти індекс, добре почати з розуміння комітів.
Починаючи з версії Git 2.23, ви можете використовувати git switchзамість git checkout. У цьому конкретному випадку ці дві команди роблять абсолютно те саме. Нова команда існує, тому що git checkoutперенасичена забагато речей; вони були розділені на дві окремі команди, git switchі git restore, щоб полегшити та безпечніше використовувати Git.
Коміти
У Git коміт зберігає повний знімок кожного файлу, про який знає Git. (Про які файли знає Git? Ми побачимо це в наступному розділі.) Ці знімки зберігаються у спеціальній, лише для читання, лише для Git, стиснутій та дедупльованій формі, яку загалом може читати лише сам Git . (У кожному коміті є більше речей, ніж просто цей знімок, але це все, що ми розглянемо тут.)
Видалення дублікатів допомагає використовувати простір: ми, як правило, міняємо лише кілька файлів, а потім робимо новий коміт. Отже, більшість файлів у коміті здебільшого збігаються з файлами у попередньому коміті. Просто повторно використовуючи ці файли безпосередньо, Git економить багато місця: якщо ми торкнулися лише одного файлу, новий коміт займає місце лише для однієї нової копії. Навіть тоді він стискається - іноді дуже стискається, хоча це насправді трапляється пізніше - так що .gitкаталог може бути насправді меншим, ніж містяться в ньому файли, як тільки вони будуть розширені до звичайних повсякденних файлів. Видалення дублікатів безпечно, оскільки створені файли заморожені на весь час. Ніхто не може змінити одну, тому коміти можуть залежати від копій один одного.
Оскільки збережені файли перебувають у цьому спеціальному, замороженому на всі часи, форматі Git, однак Git повинен розширити кожен файл у звичайну щоденну копію. Ця звичайна копія не є копією Git : це ваша копія, щоб робити як хочеш. Git просто напише їм, коли ви скажете це зробити, щоб у вас були ваші копії для роботи. Ці придатні копії знаходяться у вашому робочому дереві або робочому дереві .
Це означає, що коли ви перевіряєте певний коміт, автоматично створюється дві копії кожного файлу:
У поточному коміті Git має заморожену на весь час копію Git-ified . Ви не можете змінити цю копію (хоча, звичайно, ви можете вибрати інший коміт або зробити новий коміт).
У вашому робочому дереві є копія нормального формату. Ви можете робити все, що завгодно, використовуючи будь-яку з команд на комп’ютері.
На цьому зупиняються інші системи контролю версій (включаючи Mercurial, як уже згадувалося вище), на цих двох копіях. Ви просто модифікуєте свою копію робочого дерева, а потім фіксуєте. Git ... ні.
Індекс
Поміж цими двома копіями Git зберігає третю копію 2 кожного файлу. Ця третя копія має заморожений формат , але на відміну від замороженої копії у коміті, ви можете її змінити. Щоб змінити його, ви використовуєте git add.
У git addзасобі командного зробити індекс копія файлу збігається з роботою дерево копії . Тобто, ви говорите Git: Замініть копію замороженого формату, що не дублюється, яка зараз знаходиться в індексі, стискаючи мою оновлену копію робочого дерева, видаляючи її з копії та готуючи її до заморожування, на новий коміт. Якщо ви не використовуєте git add, індекс все ще зберігає копію замороженого формату з поточного коміту.
Коли ви запускаєте git commit, Git пакує все, що є в індексі, а потім використовувати як новий знімок. Оскільки він уже в замороженому форматі та попередньо розмножений, Git не повинен робити багато зайвої роботи.
Це також пояснює, що таке відстежувані файли . Неотслежіваемих файл являє собою файл , який знаходиться в робочому дереві , але НЕ в індексі Git і зараз . Немає значення, як це файл завершився в такому стані. Можливо, ви скопіювали його з іншого місця на комп’ютері у своє робоче дерево. Можливо, ви створили його тут свіжим. Можливо, в індексі Git була копія, але ви видалили цю копію за допомогою git rm --cached. Так чи інакше, у вашому робочому дереві є копія, але в індексі Git її немає. Якщо ви зробите новий коміт зараз, цей файл не буде в новому коміті.
Зверніть увагу, що git checkoutспочатку заповнює індекс Git з коміту, який ви перевіряєте. Отже, індекс починає відповідати коміту. Git також заповнює ваше робоче дерево з цього самого джерела. Отже, спочатку всі троє збігаються. Коли ви змінюєте файли у вашому робочому дереві, і git addвони добре, тепер індекс та ваше робоче дерево збігаються. Потім ви запускаєте, git commitі Git робить новий коміт з індексу, і тепер усі три знову збігаються.
Оскільки Git робить нові коміти з індексу, ми можемо сказати все так: індекс Git містить наступний коміт, який ви плануєте зробити. Це ігнорує розширену роль, яку індекс Git виконує під час конфліктного злиття, але ми б хотіли ігнорувати це поки що. :-)
Це все, але це все одно досить складно! Це особливо складно, оскільки немає простого способу точно побачити, що є в індексі Git. 3 Але є команда Git, яка повідомляє вам, що відбувається, таким чином, досить корисно, і ця команда є git status.
2 Технічно це насправді взагалі не є копією . Натомість це посилання на файл Git-ified, попередньо дедупльований та все інше. Тут також є багато іншого, наприклад режим, назва файлу, номер проміжного етапу та деякі дані кешу, щоб Git швидко працював. Але якщо ви не ввійдете в роботу з деякими командами низького рівня Git - git ls-files --stageі git update-indexзокрема - ви можете просто сприймати це як копію.
3git ls-files --stage команда покаже вам імена і номер поетапності кожного файлу індекс Git, але зазвичай це не дуже корисно в будь-якому випадку.
git status
git statusКоманда на насправді працює, запустивши дві окремі git diffкоманди для вас (а також робити деякі інші корисні речі, такі , як кажу вам , яку гілку ви перебуваєте).
Перший git diffпорівнює поточний коміт, який, пам’ятайте, заморожений на всі часи, з тим, що є в індексі Git. Для однакових файлів Git взагалі нічого не скаже. Для файлів, що відрізняються , Git скаже вам, що цей файл організовано для коміту . Це включає в себе все нові файли, якщо зобов'язання не sub.pyв ньому, але індекс дійсно є sub.pyв ньому, то додається, і цей файл будь-які видалені файли, які були (і є) в фіксації , але не в індекс вже ( git rmможливо,).
Другий git diffпорівнює всі файли в індексі Git з файлами у вашому робочому дереві. Для однакових файлів Git взагалі нічого не говорить. Для файлів, які відрізняються , Git буде сказати вам , що цей файл не поставив для фіксації . На відміну від першої різниці, цей конкретний список не містить абсолютно нових файлів: якщо файл untrackedіснує у вашому робочому дереві, але не в індексі Git, Git просто додає його до списку невідстежуваних файлів . 4
Врешті-решт, накопичивши ці файли, що не відстежуються, у списку, git statusбуде також оголошено імена цих файлів, але є особливий виняток: якщо ім'я файлу вказане у .gitignoreфайлі, це придушує цей останній список. Зверніть увагу, що перелік відстежуваного файлу - того, що входить до індексу Git - в a .gitignoreтут не впливає : файл знаходиться в індексі, тому його порівнюють і фіксують, навіть якщо він вказаний у .gitignore. Файл ігнорування пригнічує лише скарги щодо "файлу без відстеження". 5
4 При використанні короткої версії git status- git status -sфайли, що не відстежуються, не так відокремлені, але принцип той же. Накопичуючи подібні файли, ви також можете git statusузагальнити купу імен файлів, що не відстежуються, просто надрукувавши ім'я каталогу, іноді. Щоб отримати повний список, використовуйте git status -uallабо git status -u.
5 Перерахування файлу також змушує масово додавати багато файлових операцій, таких як git add .або git add *пропускати невідстежений файл. Ця частина стає дещо складнішою, оскільки ви можете використовувати git add --forceдля додавання файлу, який зазвичай пропускається. Є деякі інші, як правило, другорядні особливі випадки, які все додають до цього: файл .gitignoreможе бути більш правильно викликаний .git-do-not-complain-about-these-untracked-files-and-do-not-auto-add-themабо щось настільки ж громіздке. Але це занадто смішно, тож .gitignoreце так .
git add -u, git commit -aІ т.д.
Тут є кілька зручних ярликів:
git add .додасть усі оновлені файли до поточного каталогу та будь-якого підкаталогу. Це поважає .gitignore, тому, якщо на файл, який наразі не відстежується, не скаржиться git status, він не буде автоматично доданий.
git add -uавтоматично додасть усі оновлені файли в будь-яке місце у вашому робочому дереві . 6 Це стосується лише відстежуваних файлів. Зверніть увагу, що якщо ви видалили копію робочого дерева, це також призведе до видалення копії індексу ( git addробить це як частину того, щоб індекс збігався з робочим деревом ).
git add -Aце як біг git add .із верхнього рівня вашого робочого дерева (але див. виноску 6).
Крім них, ви можете бігати git commit -a, що приблизно еквівалентно 7 бігу, git add -uа потім git commit. Тобто це дає вам таку саму поведінку, яка зручна в Mercurial.
Я взагалі не раджу git commit -aшаблон: я вважаю, що його краще використовувати git statusчасто, уважно подивіться на результати , і якщо статус не такий, як ви очікували, з’ясуйте, чому це так. Використовуючи git commit -a, занадто просто випадково змінити файл і внести зміни, які ви не мали намір здійснити. Але це здебільшого питання смаку / думки.
6 Якщо ваша версія Git передує Git 2.0, будьте тут обережні: git add -uпрацює лише у поточному каталозі та підкаталогах, тому спочатку потрібно піднятися на верхній рівень вашого робочого дерева. git add -AВаріант має подібне питання.
7 Я кажу приблизно еквівалентно, оскільки git commit -aнасправді працює шляхом створення додаткового індексу та використання цього іншого індексу для фіксації. Якщо коміт працює , ви отримуєте той самий ефект, що і виконання git add -u && git commit. Якщо фіксація не працює - якщо ви змусите Git пропустити фіксацію будь-яким із багатьох способів, якими ви можете це зробити, - після цього файли не будуть git add-ed, оскільки Git викидає тимчасовий додатковий індекс і повертається до використання основного індексу .
Якщо ви використовуєте git commit --onlyтут, виникають додаткові ускладнення . У цьому випадку Git створює третій індекс, і все стає дуже складним, особливо якщо ви використовуєте хуки перед фіксацією. Це ще одна причина використовувати окремі git addоперації.